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
--- 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 \
--- 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<AncestorDirectory>());
@@ -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
--- /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();
+}
--- 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;
}
--- 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
*/
--- 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;
--- 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(
--- 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<BasicFileAttributes> ref;
+
+ WindowsPathWithAttributes(WindowsFileSystem fs,
+ WindowsPathType type,
+ String root,
+ String path,
+ BasicFileAttributes attrs)
+ {
+ super(fs, type, root, path);
+ ref = new WeakReference<BasicFileAttributes>(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;
--- 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;
}
}
--- /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<Path> 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();
+ }
+ }
+}