src/windows/classes/sun/nio/fs/WindowsPath.java

Print this page

        

@@ -25,31 +25,25 @@
 
 package sun.nio.fs;
 
 import java.nio.file.*;
 import java.nio.file.attribute.*;
-import java.nio.channels.*;
 import java.io.*;
 import java.net.URI;
-import java.security.AccessController;
 import java.util.*;
 import java.lang.ref.WeakReference;
 
 import com.sun.nio.file.ExtendedWatchEventModifier;
 
-import sun.security.util.SecurityConstants;
-import sun.misc.Unsafe;
-
 import static sun.nio.fs.WindowsNativeDispatcher.*;
 import static sun.nio.fs.WindowsConstants.*;
 
 /**
  * Windows implementation of Path
  */
 
 class WindowsPath extends AbstractPath {
-    private static final Unsafe unsafe = Unsafe.getUnsafe();
 
     // The maximum path that does not require long path prefix. On Windows
     // the maximum path is 260 minus 1 (NUL) but for directories it is 260
     // minus 12 minus 1 (to allow for the creation of a 8.3 file in the
     // directory).

@@ -227,10 +221,12 @@
             return path;
 
         // Relative path ("foo" for example)
         if (type == WindowsPathType.RELATIVE) {
             String defaultDirectory = getFileSystem().defaultDirectory();
+            if (isEmpty())
+                return defaultDirectory;
             if (defaultDirectory.endsWith("\\")) {
                 return defaultDirectory + path;
             } else {
                 StringBuilder sb =
                     new StringBuilder(defaultDirectory.length() + path.length() + 1);

@@ -284,11 +280,11 @@
         return Character.toUpperCase(root1.charAt(0)) ==
                Character.toUpperCase(root2.charAt(0));
     }
 
     // Add long path prefix to path if required
-    private static String addPrefixIfNeeded(String path) {
+    static String addPrefixIfNeeded(String path) {
         if (path.length() > 248) {
             if (path.startsWith("\\\\")) {
                 path = "\\\\?\\UNC" + path.substring(1, path.length());
             } else {
                 path = "\\\\?\\" + path;

@@ -302,14 +298,26 @@
         return fs;
     }
 
     // -- Path operations --
 
+    private boolean isEmpty() {
+        return path.length() == 0;
+    }
+
+    private WindowsPath emptyPath() {
+        return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", "");
+    }
+
     @Override
-    public Path getName() {
+    public Path getFileName() {
+        int len = path.length();
+        // represents empty path
+        if (len == 0)
+            return this;
         // represents root component only
-        if (root.length() == path.length())
+        if (root.length() == len)
             return null;
         int off = path.lastIndexOf('\\');
         if (off < root.length())
             off = root.length();
         else

@@ -338,10 +346,15 @@
             return null;
         return new WindowsPath(getFileSystem(), type, root, root);
     }
 
     // package-private
+    WindowsPathType type() {
+        return type;
+    }
+
+    // package-private
     boolean isUnc() {
         return type == WindowsPathType.UNC;
     }
 
     boolean needsSlashWhenResolving() {

@@ -353,11 +366,11 @@
     @Override
     public boolean isAbsolute() {
         return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
     }
 
-    private WindowsPath checkPath(FileRef path) {
+    static WindowsPath toWindowsPath(Path path) {
         if (path == null)
             throw new NullPointerException();
         if (!(path instanceof WindowsPath)) {
             throw new ProviderMismatchException();
         }

@@ -364,13 +377,13 @@
         return (WindowsPath)path;
     }
 
     @Override
     public WindowsPath relativize(Path obj) {
-        WindowsPath other = checkPath(obj);
+        WindowsPath other = toWindowsPath(obj);
         if (this.equals(other))
-            return null;
+            return emptyPath();
 
         // can only relativize paths of the same type
         if (this.type != other.type)
             throw new IllegalArgumentException("'other' is different type of Path");
 

@@ -408,11 +421,11 @@
     }
 
     @Override
     public Path normalize() {
         final int count = getNameCount();
-        if (count == 0)
+        if (count == 0 || isEmpty())
             return this;
 
         boolean[] ignore = new boolean[count];      // true => ignore name
         int remaining = count;                      // number of names remaining
 

@@ -486,20 +499,20 @@
         if (remaining == count)
             return this;
 
         // corner case - all names removed
         if (remaining == 0) {
-            return getRoot();
+            return (root.length() == 0) ? emptyPath() : getRoot();
         }
 
         // re-constitute the path from the remaining names.
         StringBuilder result = new StringBuilder();
         if (root != null)
             result.append(root);
         for (int i=0; i<count; i++) {
             if (!ignore[i]) {
-                result.append(getName(i).toString());
+                result.append(getName(i));
                 result.append("\\");
             }
         }
 
         // drop trailing slash in result

@@ -507,13 +520,13 @@
         return createFromNormalizedPath(getFileSystem(), result.toString());
     }
 
     @Override
     public WindowsPath resolve(Path obj) {
-        if (obj == null)
+        WindowsPath other = toWindowsPath(obj);
+        if (other.isEmpty())
             return this;
-        WindowsPath other = checkPath(obj);
         if (other.isAbsolute())
             return other;
 
         switch (other.type) {
             case RELATIVE: {

@@ -557,19 +570,18 @@
             default:
                 throw new AssertionError();
         }
     }
 
-    @Override
-    public WindowsPath resolve(String other) {
-        return resolve(getFileSystem().getPath(other));
-    }
-
     // generate offset array
     private void initOffsets() {
         if (offsets == null) {
-            ArrayList<Integer> list = new ArrayList<Integer>();
+            ArrayList<Integer> list = new ArrayList<>();
+            if (isEmpty()) {
+                // empty path considered to have one name element
+                list.add(0);
+            } else {
             int start = root.length();
             int off = root.length();
             while (off < path.length()) {
                 if (path.charAt(off) != '\\') {
                     off++;

@@ -578,10 +590,11 @@
                     start = ++off;
                 }
             }
             if (start != off)
                 list.add(start);
+            }
             synchronized (this) {
                 if (offsets == null)
                     offsets = list.toArray(new Integer[list.size()]);
             }
         }

@@ -631,16 +644,21 @@
         return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());
     }
 
     @Override
     public boolean startsWith(Path obj) {
-        WindowsPath other = checkPath(obj);
+        WindowsPath other = toWindowsPath(obj);
 
         // if this path has a root component the given path's root must match
-        if (!this.root.equalsIgnoreCase(other.root))
+        if (!this.root.equalsIgnoreCase(other.root)) {
             return false;
+        }
 
+        // empty path starts with itself
+        if (other.isEmpty())
+            return this.isEmpty();
+
         // roots match so compare elements
         int thisCount = getNameCount();
         int otherCount = other.getNameCount();
         if (otherCount <= thisCount) {
             while (--otherCount >= 0) {

@@ -655,17 +673,22 @@
         return false;
     }
 
     @Override
     public boolean endsWith(Path obj) {
-        WindowsPath other = checkPath(obj);
+        WindowsPath other = toWindowsPath(obj);
 
         // other path is longer
-        if (other.path.length() > path.length()) {
+        if (other.path.length() > this.path.length()) {
             return false;
         }
 
+        // empty path ends in itself
+        if (other.isEmpty()) {
+            return this.isEmpty();
+        }
+
         int thisCount = this.getNameCount();
         int otherCount = other.getNameCount();
 
         // given path has more elements that this path
         if (otherCount > thisCount) {

@@ -740,35 +763,10 @@
     @Override
     public String toString() {
         return path;
     }
 
-    @Override
-    public Iterator<Path> iterator() {
-        return new Iterator<Path>() {
-            private int i = 0;
-            @Override
-            public boolean hasNext() {
-                return (i < getNameCount());
-            }
-            @Override
-            public Path next() {
-                if (i < getNameCount()) {
-                    Path result = getName(i);
-                    i++;
-                    return result;
-                } else {
-                    throw new NoSuchElementException();
-                }
-            }
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-        };
-    }
-
     // -- file operations --
 
     // package-private
     long openForReadAttributeAccess(boolean followLinks)
         throws WindowsException

@@ -804,457 +802,10 @@
             sm.checkDelete(getPathForPermissionCheck());
         }
     }
 
     @Override
-    public FileStore getFileStore()
-        throws IOException
-    {
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
-            checkRead();
-        }
-        return WindowsFileStore.create(this);
-    }
-
-    /**
-     * Returns buffer with SID_AND_ATTRIBUTES structure representing the user
-     * associated with the current thread access token.
-     * FIXME - this should be cached.
-     */
-    private NativeBuffer getUserInfo() throws IOException {
-        try {
-            long hToken = WindowsSecurity.processTokenWithQueryAccess;
-            int size = GetTokenInformation(hToken, TokenUser, 0L, 0);
-            assert size > 0;
-
-            NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
-            try {
-                int newsize = GetTokenInformation(hToken, TokenUser,
-                                                  buffer.address(), size);
-                if (newsize != size)
-                    throw new AssertionError();
-                return buffer;
-            } catch (WindowsException x) {
-                buffer.release();
-                throw x;
-            }
-        } catch (WindowsException x) {
-            throw new IOException(x.getMessage());
-        }
-    }
-
-    /**
-     * Reads the file ACL and return the effective access as ACCESS_MASK
-     */
-    private int getEffectiveAccess() throws IOException {
-        // read security descriptor continaing ACL (symlinks are followed)
-        String target = WindowsLinkSupport.getFinalPath(this, true);
-        NativeBuffer aclBuffer = WindowsAclFileAttributeView
-            .getFileSecurity(target, DACL_SECURITY_INFORMATION);
-
-        // retrieves DACL from security descriptor
-        long pAcl = GetSecurityDescriptorDacl(aclBuffer.address());
-
-        // Use GetEffectiveRightsFromAcl to get effective access to file
-        try {
-            NativeBuffer userBuffer = getUserInfo();
-            try {
-                try {
-                    // SID_AND_ATTRIBUTES->pSid
-                    long pSid = unsafe.getAddress(userBuffer.address());
-                    long pTrustee = BuildTrusteeWithSid(pSid);
-                    try {
-                        return GetEffectiveRightsFromAcl(pAcl, pTrustee);
-                    } finally {
-                        LocalFree(pTrustee);
-                    }
-                } catch (WindowsException x) {
-                    throw new IOException("Unable to get effective rights from ACL: " +
-                        x.getMessage());
-                }
-            } finally {
-                userBuffer.release();
-            }
-        } finally {
-            aclBuffer.release();
-        }
-    }
-
-    @Override
-    public void checkAccess(AccessMode... modes) throws IOException {
-        // if no access modes then simply file attributes
-        if (modes.length == 0) {
-            checkRead();
-            try {
-                WindowsFileAttributes.get(this, true);
-            } catch (WindowsException exc) {
-                exc.rethrowAsIOException(this);
-            }
-            return;
-        }
-
-        boolean r = false;
-        boolean w = false;
-        boolean x = false;
-        for (AccessMode mode: modes) {
-            switch (mode) {
-                case READ : r = true; break;
-                case WRITE : w = true; break;
-                case EXECUTE : x = true; break;
-                default: throw new AssertionError("Should not get here");
-            }
-        }
-
-        int mask = 0;
-        if (r) {
-            checkRead();
-            mask |= FILE_READ_DATA;
-        }
-        if (w) {
-            checkWrite();
-            mask |= FILE_WRITE_DATA;
-        }
-        if (x) {
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null)
-                sm.checkExec(getPathForPermissionCheck());
-            mask |= FILE_EXECUTE;
-        }
-
-        if ((getEffectiveAccess() & mask) == 0)
-            throw new AccessDeniedException(
-                this.getPathForExceptionMessage(), null,
-                "Effective permissions does not allow requested access");
-
-        // for write access we neeed to check if the DOS readonly attribute
-        // and if the volume is read-only
-        if (w) {
-            try {
-                WindowsFileAttributes attrs = WindowsFileAttributes.get(this, true);
-                if (!attrs.isDirectory() && attrs.isReadOnly())
-                    throw new AccessDeniedException(
-                        this.getPathForExceptionMessage(), null,
-                        "DOS readonly attribute is set");
-            } catch (WindowsException exc) {
-                exc.rethrowAsIOException(this);
-            }
-
-            if (WindowsFileStore.create(this).isReadOnly()) {
-                throw new AccessDeniedException(
-                    this.getPathForExceptionMessage(), null, "Read-only file system");
-            }
-            return;
-        }
-    }
-
-    @Override
-    void implDelete(boolean failIfNotExists) throws IOException {
-        checkDelete();
-
-        WindowsFileAttributes attrs = null;
-        try {
-             // need to know if file is a directory or junction
-             attrs = WindowsFileAttributes.get(this, false);
-             if (attrs.isDirectory() || attrs.isDirectoryLink()) {
-                RemoveDirectory(getPathForWin32Calls());
-             } else {
-                DeleteFile(getPathForWin32Calls());
-             }
-        } catch (WindowsException x) {
-
-            // no-op if file does not exist
-            if (!failIfNotExists &&
-                (x.lastError() == ERROR_FILE_NOT_FOUND ||
-                 x.lastError() == ERROR_PATH_NOT_FOUND)) return;
-
-            if (attrs != null && attrs.isDirectory()) {
-                // ERROR_ALREADY_EXISTS is returned when attempting to delete
-                // non-empty directory on SAMBA servers.
-                if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
-                    x.lastError() == ERROR_ALREADY_EXISTS)
-                {
-                    throw new DirectoryNotEmptyException(
-                        getPathForExceptionMessage());
-                }
-            }
-            x.rethrowAsIOException(this);
-        }
-    }
-
-    @Override
-    public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter)
-        throws IOException
-    {
-        checkRead();
-        if (filter == null)
-            throw new NullPointerException();
-        return new WindowsDirectoryStream(this, filter);
-    }
-
-    @Override
-    public void implCopyTo(Path obj, CopyOption... options) throws IOException {
-        WindowsPath target = (WindowsPath)obj;
-        WindowsFileCopy.copy(this, target, options);
-    }
-
-    @Override
-    public void implMoveTo(Path obj, CopyOption... options) throws IOException {
-        WindowsPath target = (WindowsPath)obj;
-        WindowsFileCopy.move(this, target, options);
-    }
-
-    private boolean followLinks(LinkOption... options) {
-        boolean followLinks = true;
-        for (LinkOption option: options) {
-            if (option == LinkOption.NOFOLLOW_LINKS) {
-                followLinks = false;
-                continue;
-            }
-            if (option == null)
-                throw new NullPointerException();
-            throw new AssertionError("Should not get here");
-        }
-        return followLinks;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <V extends FileAttributeView> V
-        getFileAttributeView(Class<V> view, LinkOption... options)
-    {
-        if (view == null)
-            throw new NullPointerException();
-        boolean followLinks = followLinks(options);
-        if (view == BasicFileAttributeView.class)
-            return (V) WindowsFileAttributeViews.createBasicView(this, followLinks);
-        if (view == DosFileAttributeView.class)
-            return (V) WindowsFileAttributeViews.createDosView(this, followLinks);
-        if (view == AclFileAttributeView.class)
-            return (V) new WindowsAclFileAttributeView(this, followLinks);
-        if (view == FileOwnerAttributeView.class)
-            return (V) new FileOwnerAttributeViewImpl(
-                new WindowsAclFileAttributeView(this, followLinks));
-        if (view == UserDefinedFileAttributeView.class)
-            return (V) new WindowsUserDefinedFileAttributeView(this, followLinks);
-        return (V) null;
-    }
-
-    @Override
-    public DynamicFileAttributeView getFileAttributeView(String name, LinkOption... options) {
-        boolean followLinks = followLinks(options);
-        if (name.equals("basic"))
-            return WindowsFileAttributeViews.createBasicView(this, followLinks);
-        if (name.equals("dos"))
-            return WindowsFileAttributeViews.createDosView(this, followLinks);
-        if (name.equals("acl"))
-            return new WindowsAclFileAttributeView(this, followLinks);
-        if (name.equals("owner"))
-            return new FileOwnerAttributeViewImpl(
-                new WindowsAclFileAttributeView(this, followLinks));
-        if (name.equals("user"))
-            return new WindowsUserDefinedFileAttributeView(this, followLinks);
-        return null;
-    }
-
-    @Override
-    public WindowsPath createDirectory(FileAttribute<?>... attrs)
-        throws IOException
-    {
-        checkWrite();
-        WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
-        try {
-            CreateDirectory(getPathForWin32Calls(), sd.address());
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(this);
-        } finally {
-            sd.release();
-        }
-        return this;
-    }
-
-    @Override
-    public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
-                                              FileAttribute<?>... attrs)
-         throws IOException
-    {
-        WindowsSecurityDescriptor sd =
-            WindowsSecurityDescriptor.fromAttribute(attrs);
-        try {
-            return WindowsChannelFactory
-                .newFileChannel(getPathForWin32Calls(),
-                                getPathForPermissionCheck(),
-                                options,
-                                sd.address());
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(this);
-            return null;  // keep compiler happy
-        } finally {
-            sd.release();
-        }
-    }
-
-    @Override
-    public boolean isSameFile(Path obj) throws IOException {
-        if (this.equals(obj))
-            return true;
-        if (!(obj instanceof WindowsPath))  // includes null check
-            return false;
-        WindowsPath other = (WindowsPath)obj;
-
-        // check security manager access to both files
-        this.checkRead();
-        other.checkRead();
-
-        // open both files and see if they are the same
-        long h1 = 0L;
-        try {
-            h1 = this.openForReadAttributeAccess(true);
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(this);
-        }
-        try {
-            WindowsFileAttributes attrs1 = null;
-            try {
-                attrs1 = WindowsFileAttributes.readAttributes(h1);
-            } catch (WindowsException x) {
-                x.rethrowAsIOException(this);
-            }
-            long h2 = 0L;
-            try {
-                h2 = other.openForReadAttributeAccess(true);
-            } catch (WindowsException x) {
-                x.rethrowAsIOException(other);
-            }
-            try {
-                WindowsFileAttributes attrs2 = null;
-                try {
-                    attrs2 = WindowsFileAttributes.readAttributes(h2);
-                } catch (WindowsException x) {
-                    x.rethrowAsIOException(other);
-                }
-                return WindowsFileAttributes.isSameFile(attrs1, attrs2);
-            } finally {
-                CloseHandle(h2);
-            }
-        } finally {
-            CloseHandle(h1);
-        }
-    }
-
-    @Override
-    public WindowsPath createSymbolicLink(Path obj, FileAttribute<?>... attrs)
-        throws IOException
-    {
-        if (!getFileSystem().supportsLinks()) {
-            throw new UnsupportedOperationException("Symbolic links not supported "
-                + "on this operating system");
-        }
-
-        WindowsPath target = checkPath(obj);
-
-        // no attributes allowed
-        if (attrs.length > 0) {
-            WindowsSecurityDescriptor.fromAttribute(attrs);  // may throw NPE or UOE
-            throw new UnsupportedOperationException("Initial file attributes" +
-                "not supported when creating symbolic link");
-        }
-
-        // permission check
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new LinkPermission("symbolic"));
-            this.checkWrite();
-        }
-
-        /**
-         * Throw I/O exception for the drive-relative case because Windows
-         * creates a link with the resolved target for this case.
-         */
-        if (target.type == WindowsPathType.DRIVE_RELATIVE) {
-            throw new IOException("Cannot create symbolic link to working directory relative target");
-        }
-
-        /*
-         * Windows treates symbolic links to directories differently than it
-         * does to other file types. For that reason we need to check if the
-         * target is a directory (or a directory junction).
-         */
-        WindowsPath resolvedTarget;
-        if (target.type == WindowsPathType.RELATIVE) {
-            WindowsPath parent = getParent();
-            resolvedTarget = (parent == null) ? target : parent.resolve(target);
-        } else {
-            resolvedTarget = resolve(target);
-        }
-        int flags = 0;
-        try {
-            WindowsFileAttributes wattrs = WindowsFileAttributes.get(resolvedTarget, false);
-            if (wattrs.isDirectory() || wattrs.isDirectoryLink())
-                flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
-        } catch (WindowsException x) {
-            // unable to access target so assume target is not a directory
-        }
-
-        // create the link
-        try {
-            CreateSymbolicLink(getPathForWin32Calls(),
-                               addPrefixIfNeeded(target.toString()),
-                               flags);
-        } catch (WindowsException x) {
-            if (x.lastError() == ERROR_INVALID_REPARSE_DATA) {
-                x.rethrowAsIOException(this, target);
-            } else {
-                x.rethrowAsIOException(this);
-            }
-        }
-        return this;
-    }
-
-    @Override
-    public Path createLink(Path obj) throws IOException {
-        WindowsPath existing = checkPath(obj);
-
-        // permission check
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new LinkPermission("hard"));
-            this.checkWrite();
-            existing.checkWrite();
-        }
-
-        // create hard link
-        try {
-            CreateHardLink(this.getPathForWin32Calls(),
-                           existing.getPathForWin32Calls());
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(this, existing);
-        }
-
-        return this;
-    }
-
-    @Override
-    public WindowsPath readSymbolicLink() throws IOException {
-        if (!getFileSystem().supportsLinks()) {
-            throw new UnsupportedOperationException("symbolic links not supported");
-        }
-
-        // permission check
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            FilePermission perm = new FilePermission(getPathForPermissionCheck(),
-                SecurityConstants.FILE_READLINK_ACTION);
-            AccessController.checkPermission(perm);
-        }
-
-        String target = WindowsLinkSupport.readLink(this);
-        return createFromNormalizedPath(getFileSystem(), target);
-    }
-
-    @Override
     public URI toUri() {
         return WindowsUriSupport.toUri(this);
     }
 
     @Override

@@ -1280,25 +831,10 @@
         checkRead();
         String rp = WindowsLinkSupport.getRealPath(this, resolveLinks);
         return createFromNormalizedPath(getFileSystem(), rp);
     }
 
-    @Override
-    public boolean isHidden() throws IOException {
-        checkRead();
-        WindowsFileAttributes attrs = null;
-        try {
-            attrs = WindowsFileAttributes.get(this, true);
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(this);
-        }
-        // DOS hidden attribute not meaningful when set on directories
-        if (attrs.isDirectory())
-            return false;
-        return attrs.isHidden();
-    }
-
     @Override
     public WatchKey register(WatchService watcher,
                              WatchEvent.Kind<?>[] events,
                              WatchEvent.Modifier... modifiers)
         throws IOException