changeset 709:0c74f1370dfb

NamedAttributeView updates: - list() should return list instead of Iterable - list() very slow when invoked to list the named streams of large files. Also didn't work with sparse files.
author alanb
date Tue, 14 Oct 2008 09:37:04 +0100
parents 802464ea1860
children 6c350c38b283
files src/share/classes/java/nio/file/attribute/NamedAttributeView.java src/share/classes/sun/nio/fs/AbstractNamedAttributeView.java src/solaris/classes/sun/nio/fs/LinuxNamedAttributeView.java src/solaris/classes/sun/nio/fs/SolarisNamedAttributeView.java src/windows/classes/sun/nio/fs/WindowsFileSystem.java src/windows/classes/sun/nio/fs/WindowsNamedAttributeView.java src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c test/java/nio/file/attribute/NamedAttributeView/Basic.java
diffstat 9 files changed, 205 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/nio/file/attribute/NamedAttributeView.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/share/classes/java/nio/file/attribute/NamedAttributeView.java	Tue Oct 14 09:37:04 2008 +0100
@@ -26,6 +26,7 @@
 package java.nio.file.attribute;
 
 import java.nio.ByteBuffer;
+import java.util.List;
 import java.io.IOException;
 
 /**
@@ -59,6 +60,17 @@
  * supportsFileAttributeView} method may be used to test if a specific {@link
  * java.nio.file.FileStore FileStore} supports the storage of named attributes.
  *
+ * <p> Where dynamic access to file attributes is required, the {@link
+ * #getAttribute getAttribute} or {@link #readAttributes(String,String[])
+ * readAttributes(String,String[])} methods may be used to read the attribute
+ * value as if by invoking the {@link #read read} method. Upon return, the
+ * buffer's {@link ByteBuffer#position position} is the index of the first
+ * byte of the attribute value, and its {@link ByteBuffer#limit limit} is
+ * the index of the first byte that should not be read. The {@link
+ * #setAttribute setAttribute} method may be used to write the value of a
+ * user-defined/named attribute from a buffer as if by invoking the {@link
+ * #write write} method.
+ *
  * @since 1.7
  */
 
@@ -66,22 +78,18 @@
     extends FileAttributeView
 {
     /**
-     * Returns the name of the attribute view. Attribute views of this type
+     * Returns the name of this attribute view. Attribute views of this type
      * have the name {@code "xattr"}.
      */
     @Override
     String name();
 
     /**
-     * Returns an object to iterate over the names of the file's named attributes.
-     * The ordering that the names are returned is not specified. The iterator is
-     * weakly consistent. It is thread safe. It may or may not reflect updates
-     * to the list of named attributes that occur after the {@code Iterator} is
-     * created. The iterator's {@link java.util.Iterator#remove remove} method
-     * removes the named attribute for the last element returned by the iterator,
-     * as if by invoking the {@link #delete delete} method.
+     * Returns a list containing the names of the user-defined/named
+     * attributes.
      *
-     * @return  An iterator over the names of the file's named attributes
+     * @return  An unmodifiable list continaing the names of the file's
+     *          user-defined/named attributes
      *
      * @throws  IOException
      *          If an I/O error occurs
@@ -92,10 +100,10 @@
      *          or its {@link SecurityManager#checkRead(String) checkRead} method
      *          denies read access to the file.
      */
-    Iterable<String> list() throws IOException;
+    List<String> list() throws IOException;
 
     /**
-     * Returns the size of the value of the given attribute.
+     * Returns the size of the value of a user-defined/named attribute.
      *
      * @param   name
      *          The attribute name
@@ -116,7 +124,7 @@
     int size(String name) throws IOException;
 
     /**
-     * Read the value of an attribute into a buffer.
+     * Read the value of a user-defined/named attribute into a buffer.
      *
      * <p> This method reads the value of the attribute into the given buffer
      * as a sequence of bytes, failing if the number of bytes remaining in
@@ -131,11 +139,12 @@
      * Suppose we want to read a file's MIME type that is stored as a named
      * attribute:
      * <pre>
-     *    NamedAttributeView view = file.getFileAttributeView(NamedAttributeView, true);
+     *    NamedAttributeView view = file.getFileAttributeView(NamedAttributeView.class, true);
      *    String name = "user.mimetype";
      *    ByteBuffer buf = ByteBuffer.allocate(view.size(name));
      *    view.read(name, buf);
-     *    String value = Charset.defaultCharset().decode(buf.flip()).toString();
+     *    buf.flip();
+     *    String value = Charset.defaultCharset().decode(buf).toString();
      * </pre>
      *
      * @param   name
@@ -162,7 +171,7 @@
     int read(String name, ByteBuffer dst) throws IOException;
 
     /**
-     * Writes the value of an named attribute from a buffer.
+     * Writes the value of a user-defined/named attribute from a buffer.
      *
      * <p> This method writes the value of the attribute from a given buffer as
      * a sequence of bytes. The size of the value to transfer is {@code r},
@@ -186,7 +195,7 @@
      * <p> <b>Usage Example:</b>
      * Suppose we want to write a file's MIME type as a named attribute:
      * <pre>
-     *    NamedAttributeView view = file.getFileAttributeView(NamedAttributeView, true);
+     *    NamedAttributeView view = file.getFileAttributeView(NamedAttributeView.class, true);
      *    view.write("user.mimetype", Charset.defaultCharset().encode("text/html"));
      * </pre>
      *
@@ -209,7 +218,7 @@
     int write(String name, ByteBuffer src) throws IOException;
 
     /**
-     * Deletes an attribute.
+     * Deletes a user-defined/named attribute.
      *
      * @param   name
      *          The attribute name
--- a/src/share/classes/sun/nio/fs/AbstractNamedAttributeView.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/share/classes/sun/nio/fs/AbstractNamedAttributeView.java	Tue Oct 14 09:37:04 2008 +0100
@@ -54,57 +54,6 @@
         }
     }
 
-    /**
-     * Returns an Iterator for the list with a remove implementation that
-     * attempts to delete the last named attribute returned.
-     */
-    protected Iterator<String> newIterator(final List<String> list) {
-        return new Iterator<String>() {
-            private int pos = 0;
-            private String last = null;
-
-            @Override
-            public synchronized boolean hasNext() {
-                return pos < list.size();
-            }
-            @Override
-            public String next() {
-                synchronized(this) {
-                    if (pos < list.size()) {
-                        last = list.get(pos++);
-                        return last;
-                    }
-                }
-                throw new NoSuchElementException();
-            }
-            @Override
-            public void remove() {
-                String name;
-                synchronized (this) {
-                    if (last == null)
-                        throw new IllegalStateException();
-                    name = last;
-                    last = null;
-                }
-                Throwable t = null;
-                try {
-                    delete(name);
-                } catch (IOException x) {
-                    t = x;
-                } catch (SecurityException x) {
-                    t = x;
-                }
-                // Wraps given exception with a ConcurrentModificationException
-                if (t != null) {
-                    ConcurrentModificationException cme =
-                        new ConcurrentModificationException();
-                    cme.initCause(t);
-                    throw cme;
-                }
-            }
-        };
-    }
-
     @Override
     public final String name() {
         return "xattr";
--- a/src/solaris/classes/sun/nio/fs/LinuxNamedAttributeView.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/solaris/classes/sun/nio/fs/LinuxNamedAttributeView.java	Tue Oct 14 09:37:04 2008 +0100
@@ -83,7 +83,7 @@
     }
 
     @Override
-    public Iterable<String> list() throws IOException  {
+    public List<String> list() throws IOException  {
         if (System.getSecurityManager() != null)
             checkAccess(file.getPathForPermissionCheck(), true, false);
 
@@ -95,13 +95,8 @@
             for (;;) {
                 try {
                     int n = flistxattr(fd, buffer.address(), size);
-                    final List<String> list = asList(buffer.address(), n);
-                    return new Iterable<String>() {
-                        @Override
-                        public Iterator<String> iterator() {
-                            return newIterator(list);
-                        }
-                    };
+                    List<String> list = asList(buffer.address(), n);
+                    return Collections.unmodifiableList(list);
                 } catch (UnixException x) {
                     // allocate larger buffer if required
                     if (x.errno() == ERANGE && size < 32*1024) {
--- a/src/solaris/classes/sun/nio/fs/SolarisNamedAttributeView.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/solaris/classes/sun/nio/fs/SolarisNamedAttributeView.java	Tue Oct 14 09:37:04 2008 +0100
@@ -65,7 +65,7 @@
     }
 
     @Override
-    public Iterable<String> list() throws IOException  {
+    public List<String> list() throws IOException  {
         if (System.getSecurityManager() != null)
             checkAccess(file.getPathForPermissionCheck(), true, false);
 
@@ -94,12 +94,7 @@
                 } finally {
                     closedir(dp);
                 }
-                return new Iterable<String>() {
-                    @Override
-                    public Iterator<String> iterator() {
-                        return newIterator(list);
-                    }
-                };
+                return Collections.unmodifiableList(list);
             } catch (UnixException x) {
                 throw new FileSystemException(file.getPathForExecptionMessage(),
                     null, "Unable to get list of extended attributes: " +
--- a/src/windows/classes/sun/nio/fs/WindowsFileSystem.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/windows/classes/sun/nio/fs/WindowsFileSystem.java	Tue Oct 14 09:37:04 2008 +0100
@@ -48,6 +48,7 @@
     private final String defaultRoot;
 
     private final boolean supportsLinks;
+    private final boolean supportsStreamEnumeration;
 
     // package-private
     WindowsFileSystem(WindowsFileSystemProvider provider,
@@ -63,12 +64,17 @@
         this.defaultDirectory = result.path();
         this.defaultRoot = result.root();
 
-        // symbolic links available on Vista or newer
         PrivilegedAction<String> pa = new GetPropertyAction("os.version");
         String osversion = AccessController.doPrivileged(pa);
         String[] vers = osversion.split("\\.", 0);
         int major = Integer.parseInt(vers[0]);
+        int minor = Integer.parseInt(vers[1]);
+
+        // symbolic links available on Vista and newer
         supportsLinks = (major >= 6);
+
+        // enumeration of data streams available on Windows Server 2003 and newer
+        supportsStreamEnumeration = (major >= 6) || (major == 5 && minor >= 2);
     }
 
     // package-private
@@ -84,6 +90,10 @@
         return supportsLinks;
     }
 
+    boolean supportsStreamEnumeration() {
+        return supportsStreamEnumeration;
+    }
+
     @Override
     public FileSystemProvider provider() {
         return provider;
--- a/src/windows/classes/sun/nio/fs/WindowsNamedAttributeView.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/windows/classes/sun/nio/fs/WindowsNamedAttributeView.java	Tue Oct 14 09:37:04 2008 +0100
@@ -63,11 +63,37 @@
         this.followLinks = followLinks;
     }
 
-    @Override
-    public Iterable<String> list() throws IOException  {
-        if (System.getSecurityManager() != null)
-            checkAccess(file.getPathForPermissionCheck(), true, false);
+    // enumerates the file streams using FindFirstStream/FindNextStream APIs.
+    private List<String> listUsingStreamEnumeration() throws IOException {
+        List<String> list = new ArrayList<String>();
+        try {
+            FirstStream first = FindFirstStream(file.getPathForWin32Calls());
+            if (first != null) {
+                long handle = first.handle();
+                try {
+                    // first stream is always ::$DATA for files
+                    String name = first.name();
+                    if (!name.equals("::$DATA")) {
+                        String[] segs = name.split(":");
+                        list.add(segs[1]);
+                    }
+                    while ((name = FindNextStream(handle)) != null) {
+                        String[] segs = name.split(":");
+                        list.add(segs[1]);
+                    }
+                } finally {
+                    FindClose(handle);
+                }
+            }
+        } catch (WindowsException x) {
+            x.rethrowAsIOException(file);
+        }
+        return Collections.unmodifiableList(list);
+    }
 
+    // enumerates the file streams by reading the stream headers using
+    // BackupRead
+    private List<String> listUsingBackupRead() throws IOException {
         long handle = -1L;
         try {
             int flags = FILE_FLAG_BACKUP_SEMANTICS;
@@ -167,12 +193,19 @@
                 buffer.release();
             CloseHandle(handle);
         }
-        return new Iterable<String>() {
-            @Override
-            public Iterator<String> iterator() {
-                return newIterator(list);
-            }
-        };
+        return Collections.unmodifiableList(list);
+    }
+
+    @Override
+    public List<String> list() throws IOException  {
+        if (System.getSecurityManager() != null)
+            checkAccess(file.getPathForPermissionCheck(), true, false);
+        // use stream APIs on Windwos Server 2003 and newer
+        if (file.getFileSystem().supportsStreamEnumeration()) {
+            return listUsingStreamEnumeration();
+        } else {
+            return listUsingBackupRead();
+        }
     }
 
     @Override
--- a/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java	Tue Oct 14 09:37:04 2008 +0100
@@ -202,6 +202,45 @@
     static native String FindNextFile(long handle) throws WindowsException;
 
     /**
+     * HANDLE FindFirstStreamW(
+     *   LPCWSTR lpFileName,
+     *   STREAM_INFO_LEVELS InfoLevel,
+     *   LPVOID lpFindStreamData,
+     *   DWORD dwFlags
+     * )
+     */
+    static FirstStream FindFirstStream(String path) throws WindowsException {
+        NativeBuffer buffer = asNativeBuffer(path);
+        try {
+            FirstStream data = new FirstStream();
+            FindFirstStream0(buffer.address(), data);
+            if (data.handle() == WindowsConstants.INVALID_HANDLE_VALUE)
+                return null;
+            return data;
+        } finally {
+            buffer.release();
+        }
+    }
+    static class FirstStream {
+        private long handle;
+        private String name;
+
+        private FirstStream() { }
+        public long handle()    { return handle; }
+        public String name()    { return name; }
+    }
+    private static native void FindFirstStream0(long lpFileName, FirstStream obj)
+        throws WindowsException;
+
+    /*
+     * FindNextStreamW(
+     *   HANDLE hFindStream,
+     *   LPVOID lpFindStreamData
+     * )
+     */
+    static native String FindNextStream(long handle) throws WindowsException;
+
+    /**
      * FindClose(
      *   HANDLE hFindFile
      * )
--- a/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c	Tue Oct 14 09:21:00 2008 +0100
+++ b/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c	Tue Oct 14 09:37:04 2008 +0100
@@ -50,6 +50,9 @@
 static jfieldID findFirst_name;
 static jfieldID findFirst_reserved0;
 
+static jfieldID findStream_handle;
+static jfieldID findStream_name;
+
 static jfieldID volumeInfo_fsName;
 static jfieldID volumeInfo_volName;
 static jfieldID volumeInfo_volSN;
@@ -76,6 +79,19 @@
 /**
  * Win32 APIs not defined in Visual Studio 2003 header files
  */
+
+typedef enum {
+  FindStreamInfoStandard
+} MY_STREAM_INFO_LEVELS;
+
+typedef struct _MY_WIN32_FIND_STREAM_DATA {
+  LARGE_INTEGER StreamSize;
+  WCHAR cStreamName[MAX_PATH + 36];
+} MY_WIN32_FIND_STREAM_DATA;
+
+typedef HANDLE (WINAPI* FindFirstStream_Proc)(LPCWSTR, MY_STREAM_INFO_LEVELS, LPVOID, DWORD);
+typedef BOOL (WINAPI* FindNextStream_Proc)(HANDLE, LPVOID);
+
 typedef BOOLEAN (WINAPI* CreateSymbolicLinkProc) (LPCWSTR, LPCWSTR, DWORD);
 typedef BOOL (WINAPI* CreateHardLinkProc) (LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
 typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD);
@@ -84,6 +100,9 @@
 typedef BOOL (WINAPI* ConvertStringSidToSidProc) (LPWSTR, PSID*);
 typedef DWORD (WINAPI* GetLengthSidProc) (PSID);
 
+static FindFirstStream_Proc FindFirstStream_func;
+static FindNextStream_Proc FindNextStream_func;
+
 static CreateSymbolicLinkProc CreateSymbolicLink_func;
 static CreateHardLinkProc CreateHardLink_func;
 static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func;
@@ -118,6 +137,13 @@
     findFirst_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
     findFirst_reserved0 = (*env)->GetFieldID(env, clazz, "reserved0", "I");
 
+    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstStream");
+    if (clazz == NULL) {
+        return;
+    }
+    findStream_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
+    findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
+
     clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation");
     if (clazz == NULL) {
         return;
@@ -167,6 +193,10 @@
 
     h = LoadLibrary("kernel32");
     if (h != INVALID_HANDLE_VALUE) {
+        FindFirstStream_func =
+            (FindFirstStream_Proc)GetProcAddress(h, "FindFirstStreamW");
+        FindNextStream_func =
+            (FindNextStream_Proc)GetProcAddress(h, "FindNextStreamW");
         CreateSymbolicLink_func =
             (CreateSymbolicLinkProc)GetProcAddress(h, "CreateSymbolicLinkW");
         CreateHardLink_func =
@@ -369,6 +399,57 @@
     }
 }
 
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstStream0(JNIEnv* env, jclass this,
+    jlong address, jobject obj)
+{
+    MY_WIN32_FIND_STREAM_DATA data;
+    LPCWSTR lpFileName = jlong_to_ptr(address);
+    HANDLE handle;
+
+    if (FindFirstStream_func == NULL) {
+        JNU_ThrowInternalError(env, "Should not get here");
+        return;
+    }
+
+    handle = (*FindFirstStream_func)(lpFileName, FindStreamInfoStandard, &data, 0);
+    if (handle != INVALID_HANDLE_VALUE) {
+        jstring name = (*env)->NewString(env, data.cStreamName, wcslen(data.cStreamName));
+        if (name == NULL)
+            return;
+        (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle));
+        (*env)->SetObjectField(env, obj, findStream_name, name);
+    } else {
+        if (GetLastError() == ERROR_HANDLE_EOF) {
+             (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle));
+        } else {
+            throwWindowsException(env, GetLastError());
+        }
+    }
+
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindNextStream(JNIEnv* env, jclass this,
+    jlong handle)
+{
+    MY_WIN32_FIND_STREAM_DATA data;
+    HANDLE h = (HANDLE)jlong_to_ptr(handle);
+
+    if (FindNextStream_func == NULL) {
+        JNU_ThrowInternalError(env, "Should not get here");
+        return NULL;
+    }
+
+    if ((*FindNextStream_func)(h, &data) != 0) {
+        return (*env)->NewString(env, data.cStreamName, wcslen(data.cStreamName));
+    } else {
+        if (GetLastError() != ERROR_HANDLE_EOF)
+            throwWindowsException(env, GetLastError());
+        return NULL;
+    }
+}
+
 
 JNIEXPORT void JNICALL
 Java_sun_nio_fs_WindowsNativeDispatcher_FindClose(JNIEnv* env, jclass this,
--- a/test/java/nio/file/attribute/NamedAttributeView/Basic.java	Tue Oct 14 09:21:00 2008 +0100
+++ b/test/java/nio/file/attribute/NamedAttributeView/Basic.java	Tue Oct 14 09:37:04 2008 +0100
@@ -103,21 +103,6 @@
         view.delete(ATTR_NAME);
         if (hasAttribute(view, ATTR_NAME))
             throw new RuntimeException("Attribute name in list");
-
-        // Test: iterator's remove method
-        buf.clear();
-        buf.put(ATTR_VALUE.getBytes()).flip();
-        view.write(ATTR_NAME, buf);
-        Iterator<String> iter = view.list().iterator();
-        while (iter.hasNext()) {
-            String name = iter.next();
-            if (name.equals(ATTR_NAME)) {
-                iter.remove();
-                break;
-            }
-        }
-        if (hasAttribute(view, ATTR_NAME))
-            throw new RuntimeException("Attribute name in list");
     }
 
     static void miscTests(Path file) throws IOException {