# HG changeset patch # User alanb # Date 1235466702 0 # Node ID 5e6af6d106cb2699c22a113d071c899349ec816f # Parent 2cd4a0aa917f4296255ec5cdf1afc30b83d9720b 6808647: (file) Paths.get("C:").newDirectoryStream() iterates over Path elements with additional slash [win] 6808648: (file) Files.walkFileTree should obtain file attributes during iteration [win] Reviewed-by: sherman diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/make/java/nio/FILES_java.gmk --- a/jdk/make/java/nio/FILES_java.gmk Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/make/java/nio/FILES_java.gmk Tue Feb 24 09:11:42 2009 +0000 @@ -252,6 +252,7 @@ sun/nio/fs/AbstractUserDefinedFileAttributeView.java \ sun/nio/fs/AbstractWatchKey.java \ sun/nio/fs/AbstractWatchService.java \ + sun/nio/fs/BasicFileAttributesHolder.java \ sun/nio/fs/Cancellable.java \ sun/nio/fs/DefaultFileSystemProvider.java \ sun/nio/fs/DefaultFileTypeDetector.java \ diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/share/classes/java/nio/file/FileTreeWalker.java --- a/jdk/src/share/classes/java/nio/file/FileTreeWalker.java Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/share/classes/java/nio/file/FileTreeWalker.java Tue Feb 24 09:11:42 2009 +0000 @@ -28,6 +28,7 @@ import java.nio.file.attribute.*; import java.io.IOException; import java.util.*; +import sun.nio.fs.BasicFileAttributesHolder; /** * Simple file tree walker that works in a similar manner to nftw(3C). @@ -65,6 +66,10 @@ * Walk file tree starting at the given file */ void walk(Path start, int maxDepth) { + // don't use attributes of starting file as they may be stale + if (start instanceof BasicFileAttributesHolder) { + ((BasicFileAttributesHolder)start).invalidate(); + } FileVisitResult result = walk(start, maxDepth, new ArrayList()); @@ -75,11 +80,9 @@ /** * @param file - * The directory to visit - * @param path - * list of directories that is relative path from starting file + * the directory to visit * @param depth - * Depth remaining + * depth remaining * @param ancestors * use when cycle detection is enabled */ @@ -91,28 +94,36 @@ if (depth-- < 0) return FileVisitResult.CONTINUE; + // if attributes are cached then use them if possible BasicFileAttributes attrs = null; + if (file instanceof BasicFileAttributesHolder) { + BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get(); + if (!followLinks || !cached.isSymbolicLink()) + attrs = cached; + } IOException exc = null; // attempt to get attributes of file. If fails and we are following // links then a link target might not exist so get attributes of link - try { + if (attrs == null) { try { - attrs = Attributes.readBasicFileAttributes(file, linkOptions); - } catch (IOException x1) { - if (followLinks) { - try { - attrs = Attributes - .readBasicFileAttributes(file, LinkOption.NOFOLLOW_LINKS); - } catch (IOException x2) { - exc = x2; + try { + attrs = Attributes.readBasicFileAttributes(file, linkOptions); + } catch (IOException x1) { + if (followLinks) { + try { + attrs = Attributes + .readBasicFileAttributes(file, LinkOption.NOFOLLOW_LINKS); + } catch (IOException x2) { + exc = x2; + } + } else { + exc = x1; } - } else { - exc = x1; } + } catch (SecurityException x) { + return FileVisitResult.CONTINUE; } - } catch (SecurityException x) { - return FileVisitResult.CONTINUE; } // unable to get attributes of file diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/share/classes/sun/nio/fs/BasicFileAttributesHolder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/nio/fs/BasicFileAttributesHolder.java Tue Feb 24 09:11:42 2009 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.fs; + +import java.nio.file.attribute.BasicFileAttributes; + +/** + * Implemented by objects that may hold or cache the attributes of a file. + */ + +public interface BasicFileAttributesHolder { + /** + * Returns cached attributes (may be null). If file is a symbolic link then + * the attributes are the link attributes and not the final target of the + * file. + */ + BasicFileAttributes get(); + + /** + * Invalidates cached attributes + */ + void invalidate(); +} diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java --- a/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java Tue Feb 24 09:11:42 2009 +0000 @@ -26,6 +26,7 @@ package sun.nio.fs; import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Iterator; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; @@ -49,6 +50,9 @@ // first entry in the directory private final String firstName; + // buffer for WIN32_FIND_DATA structure that receives information about file + private final NativeBuffer findDataBuffer; + private final Object closeLock = new Object(); // need closeLock to access these @@ -75,6 +79,7 @@ FirstFile first = FindFirstFile(search); this.handle = first.handle(); this.firstName = first.name(); + this.findDataBuffer = WindowsFileAttributes.getBufferForFindData(); } catch (WindowsException x) { if (x.lastError() == ERROR_DIRECTORY) { throw new NotDirectoryException(dir.getPathForExceptionMessage()); @@ -95,6 +100,7 @@ return; isOpen = false; } + findDataBuffer.release(); try { FindClose(handle); } catch (WindowsException x) { @@ -133,11 +139,19 @@ } // applies filter and also ignores "." and ".." - private Path acceptEntry(String s) { + private Path acceptEntry(String s, BasicFileAttributes attrs) { if (s.equals(".") || s.equals("..")) return null; + if (dir.needsSlashWhenResolving()) { + StringBuilder sb = new StringBuilder(dir.toString()); + sb.append('\\'); + sb.append(s); + s = sb.toString(); + } else { + s = dir + s; + } Path entry = WindowsPath - .createFromNormalizedPath(dir.getFileSystem(), dir + "\\" + s); + .createFromNormalizedPath(dir.getFileSystem(), s, attrs); if (filter.accept(entry)) { return entry; } else { @@ -149,21 +163,27 @@ private Path readNextEntry() { // handle first element returned by search if (first != null) { - nextEntry = acceptEntry(first); + nextEntry = acceptEntry(first, null); first = null; if (nextEntry != null) return nextEntry; } - String name = null; for (;;) { + String name = null; + WindowsFileAttributes attrs; + // synchronize on closeLock to prevent close while reading synchronized (closeLock) { if (!isOpen) throwAsConcurrentModificationException(new IllegalStateException("Directory stream is closed")); try { - name = FindNextFile(handle); + name = FindNextFile(handle, findDataBuffer.address()); + if (name == null) { + // NO_MORE_FILES + return null; + } } catch (WindowsException x) { try { x.rethrowAsIOException(dir); @@ -171,13 +191,16 @@ throwAsConcurrentModificationException(ioe); } } + + // grab the attributes from the WIN32_FIND_DATA structure + // (needs to be done while holding closeLock because close + // will release the buffer) + attrs = WindowsFileAttributes + .fromFindData(findDataBuffer.address()); } - // EOF - if (name == null) - return null; - - Path entry = acceptEntry(name); + // return entry if accepted by filter + Path entry = acceptEntry(name, attrs); if (entry != null) return entry; } diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java --- a/jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java Tue Feb 24 09:11:42 2009 +0000 @@ -87,6 +87,29 @@ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH = 28; private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW = 32; + /** + * typedef struct _WIN32_FIND_DATA { + * DWORD dwFileAttributes; + * FILETIME ftCreationTime; + * FILETIME ftLastAccessTime; + * FILETIME ftLastWriteTime; + * DWORD nFileSizeHigh; + * DWORD nFileSizeLow; + * DWORD dwReserved0; + * DWORD dwReserved1; + * TCHAR cFileName[MAX_PATH]; + * TCHAR cAlternateFileName[14]; + * } WIN32_FIND_DATA; + */ + private static final short SIZEOF_FIND_DATA = 592; + private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0; + private static final short OFFSETOF_FIND_DATA_CREATETIME = 4; + private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12; + private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20; + private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28; + private static final short OFFSETOF_FIND_DATA_SIZELOW = 32; + private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36; + // indicates if accurate metadata is required (interesting on NTFS only) private static final boolean ensureAccurateMetadata; static { @@ -210,6 +233,41 @@ 0); // fileIndexLow } + + /** + * Allocates a native buffer for a WIN32_FIND_DATA structure + */ + static NativeBuffer getBufferForFindData() { + return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA); + } + + /** + * Create a WindowsFileAttributes from a WIN32_FIND_DATA structure + */ + static WindowsFileAttributes fromFindData(long address) { + int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES); + long creationTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME)); + long lastAccessTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME)); + long lastWriteTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME)); + long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32) + + (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL); + int reparseTag = ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) ? + + unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0; + return new WindowsFileAttributes(fileAttrs, + creationTime, + lastAccessTime, + lastWriteTime, + size, + reparseTag, + 1, // linkCount + 0, // volSerialNumber + 0, // fileIndexHigh + 0); // fileIndexLow + } + /** * Reads the attributes of an open file */ diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/windows/classes/sun/nio/fs/WindowsFileSystem.java --- a/jdk/src/windows/classes/sun/nio/fs/WindowsFileSystem.java Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsFileSystem.java Tue Feb 24 09:11:42 2009 +0000 @@ -236,11 +236,9 @@ @Override public Path getPath(String path) { - WindowsPathParser.Result result = WindowsPathParser.parse(path); - return new WindowsPath(this, result.type(), result.root(), result.path()); + return WindowsPath.parse(this, path); } - @Override public UserPrincipalLookupService getUserPrincipalLookupService() { return theLookupService; diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java --- a/jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java Tue Feb 24 09:11:42 2009 +0000 @@ -211,9 +211,10 @@ * LPWIN32_FIND_DATA lpFindFileData * ) * - * @return lpFindFileData->cFileName + * @return lpFindFileData->cFileName or null */ - static native String FindNextFile(long handle) throws WindowsException; + static native String FindNextFile(long handle, long address) + throws WindowsException; /** * HANDLE FindFirstStreamW( diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/windows/classes/sun/nio/fs/WindowsPath.java --- a/jdk/src/windows/classes/sun/nio/fs/WindowsPath.java Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsPath.java Tue Feb 24 09:11:42 2009 +0000 @@ -83,10 +83,10 @@ /** * Initializes a new instance of this class. */ - WindowsPath(WindowsFileSystem fs, - WindowsPathType type, - String root, - String path) + private WindowsPath(WindowsFileSystem fs, + WindowsPathType type, + String root, + String path) { this.fs = fs; this.type = type; @@ -95,7 +95,7 @@ } /** - * Creates a WindowsPath by parsing the given path. + * Creates a Path by parsing the given path. */ static WindowsPath parse(WindowsFileSystem fs, String path) { WindowsPathParser.Result result = WindowsPathParser.parse(path); @@ -103,18 +103,71 @@ } /** - * Creates a WindowsPath from a given path that is known to be normalized. + * Creates a Path from a given path that is known to be normalized. */ - static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, String path) { + static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, + String path, + BasicFileAttributes attrs) + { try { WindowsPathParser.Result result = WindowsPathParser.parseNormalizedPath(path); - return new WindowsPath(fs, result.type(), result.root(), result.path()); + if (attrs == null) { + return new WindowsPath(fs, + result.type(), + result.root(), + result.path()); + } else { + return new WindowsPathWithAttributes(fs, + result.type(), + result.root(), + result.path(), + attrs); + } } catch (InvalidPathException x) { throw new AssertionError(x.getMessage()); } } + /** + * Creates a WindowsPath from a given path that is known to be normalized. + */ + static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, + String path) + { + return createFromNormalizedPath(fs, path, null); + } + + /** + * Special implementation with attached/cached attributes (used to quicken + * file tree traveral) + */ + private static class WindowsPathWithAttributes + extends WindowsPath implements BasicFileAttributesHolder + { + final WeakReference ref; + + WindowsPathWithAttributes(WindowsFileSystem fs, + WindowsPathType type, + String root, + String path, + BasicFileAttributes attrs) + { + super(fs, type, root, path); + ref = new WeakReference(attrs); + } + + @Override + public BasicFileAttributes get() { + return ref.get(); + } + + @Override + public void invalidate() { + ref.clear(); + } + } + // use this message when throwing exceptions String getPathForExceptionMessage() { return path; @@ -290,6 +343,12 @@ return type == WindowsPathType.UNC; } + boolean needsSlashWhenResolving() { + if (path.endsWith("\\")) + return false; + return path.length() > root.length(); + } + @Override public boolean isAbsolute() { return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC; diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c --- a/jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c Mon Feb 23 10:36:19 2009 +0000 +++ b/jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c Tue Feb 24 09:11:42 2009 +0000 @@ -392,16 +392,16 @@ JNIEXPORT jstring JNICALL Java_sun_nio_fs_WindowsNativeDispatcher_FindNextFile(JNIEnv* env, jclass this, - jlong handle) + jlong handle, jlong dataAddress) { - WIN32_FIND_DATAW data; HANDLE h = (HANDLE)jlong_to_ptr(handle); + WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress); - if (FindNextFileW(h, &data) != 0) { - return (*env)->NewString(env, data.cFileName, wcslen(data.cFileName)); + if (FindNextFileW(h, data) != 0) { + return (*env)->NewString(env, data->cFileName, wcslen(data->cFileName)); } else { - if (GetLastError() != ERROR_NO_MORE_FILES) - throwWindowsException(env, GetLastError()); + if (GetLastError() != ERROR_NO_MORE_FILES) + throwWindowsException(env, GetLastError()); return NULL; } } diff -r 2cd4a0aa917f -r 5e6af6d106cb jdk/test/java/nio/file/DirectoryStream/DriveLetter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/file/DirectoryStream/DriveLetter.java Tue Feb 24 09:11:42 2009 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6808647 + * @summary Checks that a DirectoryStream's iterator returns the expected + * path when opening a directory by specifying only the drive letter. + * @library .. + */ + +import java.nio.file.*; +import java.io.File; +import java.io.IOException; + +public class DriveLetter { + + public static void main(String[] args) throws IOException { + String os = System.getProperty("os.name"); + if (!os.startsWith("Windows")) { + System.out.println("This is Windows specific test"); + return; + } + String here = System.getProperty("user.dir"); + if (here.length() < 2 || here.charAt(1) != ':') + throw new RuntimeException("Unable to determine drive letter"); + + // create temporary file in current directory + File tempFile = File.createTempFile("foo", "tmp", new File(here)); + try { + // we should expect C:foo.tmp to be returned by iterator + String drive = here.substring(0, 2); + Path expected = Paths.get(drive).resolve(tempFile.getName()); + + boolean found = false; + DirectoryStream stream = Paths.get(drive).newDirectoryStream(); + try { + for (Path file : stream) { + if (file.equals(expected)) { + found = true; + break; + } + } + } finally { + stream.close(); + } + if (!found) + throw new RuntimeException("Temporary file not found???"); + + } finally { + tempFile.delete(); + } + } +}