src/solaris/classes/sun/nio/fs/UnixPath.java

Print this page

        

@@ -25,19 +25,15 @@
 
 package sun.nio.fs;
 
 import java.nio.*;
 import java.nio.file.*;
-import java.nio.file.attribute.*;
 import java.nio.charset.*;
-import java.nio.channels.*;
-import java.security.AccessController;
 import java.io.*;
 import java.net.URI;
 import java.util.*;
 import java.lang.ref.SoftReference;
-import sun.security.util.SecurityConstants;
 
 import static sun.nio.fs.UnixNativeDispatcher.*;
 import static sun.nio.fs.UnixConstants.*;
 
 /**

@@ -77,12 +73,10 @@
 
     // package-private
     // removes redundant slashes and check input for invalid characters
     static String normalizeAndCheck(String input) {
         int n = input.length();
-        if (n == 0)
-            throw new InvalidPathException(input, "Path is empty");
         char prevChar = 0;
         for (int i=0; i < n; i++) {
             char c = input.charAt(i);
             if ((c == '/') && (prevChar == '/'))
                 return normalize(input, n, i - 1);

@@ -172,13 +166,19 @@
         // resolve against default directory if required (chdir allowed or
         // file system default directory is not working directory)
         if (getFileSystem().needToResolveAgainstDefaultDirectory()) {
             return resolve(getFileSystem().defaultDirectory(), path);
         } else {
+            if (!isEmpty()) {
             return path;
+            } else {
+                // empty path case will access current directory
+                byte[] here = { '.' };
+                return here;
         }
     }
+    }
 
     // use this message when throwing exceptions
     String getPathForExecptionMessage() {
         return toString();
     }

@@ -191,11 +191,11 @@
             return toString();
         }
     }
 
     // Checks that the given file is a UnixPath
-    private UnixPath checkPath(FileRef obj) {
+    static UnixPath toUnixPath(Path obj) {
         if (obj == null)
             throw new NullPointerException();
         if (!(obj instanceof UnixPath))
             throw new ProviderMismatchException();
         return (UnixPath)obj;

@@ -207,18 +207,23 @@
             int count, index;
 
             // count names
             count = 0;
             index = 0;
+            if (isEmpty()) {
+                // empty path has one name
+                count = 1;
+            } else {
             while (index < path.length) {
                 byte c = path[index++];
                 if (c != '/') {
                     count++;
                     while (index < path.length && path[index] != '/')
                         index++;
                 }
             }
+            }
 
             // populate offsets
             int[] result = new int[count];
             count = 0;
             index = 0;

@@ -237,33 +242,46 @@
                     offsets = result;
             }
         }
     }
 
+    // returns {@code true} if this path is an empty path
+    private boolean isEmpty() {
+        return path.length == 0;
+    }
+    
+    // returns an empty path
+    private UnixPath emptyPath() {
+        return new UnixPath(getFileSystem(), new byte[0]);
+    }
+
     @Override
     public UnixFileSystem getFileSystem() {
         return fs;
     }
 
     @Override
     public UnixPath getRoot() {
-        if (path[0] == '/') {
+        if (path.length > 0 && path[0] == '/') {
             return getFileSystem().rootDirectory();
         } else {
             return null;
         }
     }
 
     @Override
-    public UnixPath getName() {
+    public UnixPath getFileName() {
         initOffsets();
 
         int count = offsets.length;
+        
+        // no elements so no name
         if (count == 0)
-            return null;  // no elements so no name
+            return null;
 
-        if (count == 1 && path[0] != '/')
+        // one name element and no root component
+        if (count == 1 && path.length > 0 && path[0] != '/')
             return this;
 
         int lastOffset = offsets[count-1];
         int len = path.length - lastOffset;
         byte[] result = new byte[len];

@@ -347,61 +365,62 @@
         return new UnixPath(getFileSystem(), result);
     }
 
     @Override
     public boolean isAbsolute() {
-        return (path[0] == '/');
+        return (path.length > 0 && path[0] == '/');
     }
 
     // Resolve child against given base
     private static byte[] resolve(byte[] base, byte[] child) {
-        if (child[0] == '/')
+        int baseLength = base.length;
+        int childLength = child.length;
+        if (childLength == 0)
+            return base;        
+        if (baseLength == 0 || child[0] == '/')
             return child;
         byte[] result;
-        if (base.length == 1 && base[0] == '/') {
-            result = new byte[child.length + 1];
+        if (baseLength == 1 && base[0] == '/') {
+            result = new byte[childLength + 1];
             result[0] = '/';
-            System.arraycopy(child, 0, result, 1, child.length);
+            System.arraycopy(child, 0, result, 1, childLength);
         } else {
-            result = new byte[base.length + 1 + child.length];
-            System.arraycopy(base, 0, result, 0, base.length);
+            result = new byte[baseLength + 1 + childLength];
+            System.arraycopy(base, 0, result, 0, baseLength);
             result[base.length] = '/';
-            System.arraycopy(child, 0, result,  base.length+1, child.length);
+            System.arraycopy(child, 0, result, baseLength+1, childLength);
         }
         return result;
     }
 
     @Override
     public UnixPath resolve(Path obj) {
-        if (obj == null)
-            return this;
-        byte[] other = checkPath(obj).path;
-        if (other[0] == '/')
+        byte[] other = toUnixPath(obj).path;
+        if (other.length > 0 && other[0] == '/')
             return ((UnixPath)obj);
         byte[] result = resolve(path, other);
         return new UnixPath(getFileSystem(), result);
     }
 
-    @Override
-    public UnixPath resolve(String other) {
-        return resolve(new UnixPath(getFileSystem(), other));
-    }
-
     UnixPath resolve(byte[] other) {
         return resolve(new UnixPath(getFileSystem(), other));
     }
 
     @Override
     public UnixPath relativize(Path obj) {
-        UnixPath other = checkPath(obj);
+        UnixPath other = toUnixPath(obj);
         if (other.equals(this))
-            return null;
+            return emptyPath();
 
         // can only relativize paths of the same type
         if (this.isAbsolute() != other.isAbsolute())
             throw new IllegalArgumentException("'other' is different type of Path");
 
+        // this path is the empty path
+        if (this.isEmpty())
+            return other;
+
         int bn = this.getNameCount();
         int cn = other.getNameCount();
 
         // skip matching names
         int n = (bn > cn) ? cn : bn;

@@ -417,18 +436,31 @@
             // remaining name components in other
             UnixPath remainder = other.subpath(i, cn);
             if (dotdots == 0)
                 return remainder;
 
+            // other is the empty path
+            boolean isOtherEmpty = other.isEmpty();
+
             // result is a  "../" for each remaining name in base
-            // followed by the remaining names in other
-            byte[] result = new byte[dotdots*3 + remainder.path.length];
+            // followed by the remaining names in other. If the remainder is
+            // the empty path then we don't add the final trailing slash.
+            int len = dotdots*3 + remainder.path.length;
+            if (isOtherEmpty) {
+                assert remainder.isEmpty();
+                len--;
+            }
+            byte[] result = new byte[len];
             int pos = 0;
             while (dotdots > 0) {
                 result[pos++] = (byte)'.';
                 result[pos++] = (byte)'.';
+                if (isOtherEmpty) {
+                    if (dotdots > 1) result[pos++] = (byte)'/';
+                } else {
                 result[pos++] = (byte)'/';
+                }
                 dotdots--;
             }
             System.arraycopy(remainder.path, 0, result, pos, remainder.path.length);
             return new UnixPath(getFileSystem(), result);
         } else {

@@ -455,11 +487,11 @@
 
         boolean[] ignore = new boolean[count];      // true => ignore name
         int[] size = new int[count];                // length of name
         int remaining = count;                      // number of names remaining
         boolean hasDotDot = false;                  // has at least one ..
-        boolean isAbsolute = path[0] == '/';
+        boolean isAbsolute = isAbsolute();
 
         // first pass:
         //   1. compute length of names
         //   2. mark all occurences of "." to ignore
         //   3. and look for any occurences of ".."

@@ -540,11 +572,11 @@
         if (remaining == count)
             return this;
 
         // corner case - all names removed
         if (remaining == 0) {
-            return isAbsolute ? getFileSystem().rootDirectory() : null;
+            return isAbsolute ? getFileSystem().rootDirectory() : emptyPath();
         }
 
         // compute length of result
         int len = remaining - 1;
         if (isAbsolute)

@@ -572,22 +604,23 @@
         return new UnixPath(getFileSystem(), result);
     }
 
     @Override
     public boolean startsWith(Path other) {
-        UnixPath that = checkPath(other);
+        UnixPath that = toUnixPath(other);
 
         // other path is longer
         if (that.path.length > path.length)
             return false;
 
         int thisOffsetCount = getNameCount();
         int thatOffsetCount = that.getNameCount();
 
         // other path has no name elements
-        if (thatOffsetCount == 0 && this.isAbsolute())
-            return true;
+        if (thatOffsetCount == 0 && this.isAbsolute()) {
+            return that.isEmpty() ? false : true;
+        }
 
         // given path has more elements that this path
         if (thatOffsetCount > thisOffsetCount)
             return false;
 

@@ -620,19 +653,23 @@
         return true;
     }
 
     @Override
     public boolean endsWith(Path other) {
-        UnixPath that = checkPath(other);
+        UnixPath that = toUnixPath(other);
 
         int thisLen = path.length;
         int thatLen = that.path.length;
 
         // other path is longer
         if (thatLen > thisLen)
             return false;
 
+        // other path is the empty path
+        if (thisLen > 0 && thatLen == 0)
+            return false;
+
         // other path is absolute so this path must be absolute
         if (that.isAbsolute() && !this.isAbsolute())
             return false;
 
         int thisOffsetCount = getNameCount();

@@ -719,36 +756,10 @@
         if (stringValue == null)
             stringValue = new String(path);     // platform encoding
         return stringValue;
     }
 
-    @Override
-    public Iterator<Path> iterator() {
-        initOffsets();
-        return new Iterator<Path>() {
-            int i = 0;
-            @Override
-            public boolean hasNext() {
-                return (i < offsets.length);
-            }
-            @Override
-            public Path next() {
-                if (i < offsets.length) {
-                    Path result = getName(i);
-                    i++;
-                    return result;
-                } else {
-                    throw new NoSuchElementException();
-                }
-            }
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-        };
-    }
-
     // -- file operations --
 
     // package-private
     int openForAttributeAccess(boolean followLinks) throws IOException {
         int flags = O_RDONLY;

@@ -768,11 +779,10 @@
             x.rethrowAsIOException(this);
             return -1; // keep compile happy
         }
     }
 
-
     void checkRead() {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null)
             sm.checkRead(getPathForPermissionCheck());
     }

@@ -788,300 +798,10 @@
         if (sm != null)
             sm.checkDelete(getPathForPermissionCheck());
     }
 
     @Override
-    public FileStore getFileStore()
-        throws IOException
-    {
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
-            checkRead();
-        }
-        return getFileSystem().getFileStore(this);
-    }
-
-    @Override
-    public void checkAccess(AccessMode... modes) throws IOException {
-        boolean e = false;
-        boolean r = false;
-        boolean w = false;
-        boolean x = false;
-
-        if (modes.length == 0) {
-            e = true;
-        } else {
-            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 mode = 0;
-        if (e || r) {
-            checkRead();
-            mode |= (r) ? R_OK : F_OK;
-        }
-        if (w) {
-            checkWrite();
-            mode |= W_OK;
-        }
-        if (x) {
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                // not cached
-                sm.checkExec(getPathForPermissionCheck());
-            }
-            mode |= X_OK;
-        }
-        try {
-            access(this, mode);
-        } catch (UnixException exc) {
-            exc.rethrowAsIOException(this);
-        }
-    }
-
-    @Override
-    void implDelete(boolean failIfNotExists) throws IOException {
-        checkDelete();
-
-        // need file attributes to know if file is directory
-        UnixFileAttributes attrs = null;
-        try {
-            attrs = UnixFileAttributes.get(this, false);
-            if (attrs.isDirectory()) {
-                rmdir(this);
-            } else {
-                unlink(this);
-            }
-        } catch (UnixException x) {
-            // no-op if file does not exist
-            if (!failIfNotExists && x.errno() == ENOENT)
-                return;
-
-            // DirectoryNotEmptyException if not empty
-            if (attrs != null && attrs.isDirectory() &&
-                (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
-                throw new DirectoryNotEmptyException(getPathForExecptionMessage());
-
-            x.rethrowAsIOException(this);
-        }
-    }
-
-    @Override
-    public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter)
-        throws IOException
-    {
-        if (filter == null)
-            throw new NullPointerException();
-        checkRead();
-
-        // can't return SecureDirectoryStream on kernels that don't support
-        // openat, etc.
-        if (!supportsAtSysCalls()) {
-            try {
-                long ptr = opendir(this);
-                return new UnixDirectoryStream(this, ptr, filter);
-            } catch (UnixException x) {
-                if (x.errno() == ENOTDIR)
-                    throw new NotDirectoryException(getPathForExecptionMessage());
-                x.rethrowAsIOException(this);
-            }
-        }
-
-        // open directory and dup file descriptor for use by
-        // opendir/readdir/closedir
-        int dfd1 = -1;
-        int dfd2 = -1;
-        long dp = 0L;
-        try {
-            dfd1 = open(this, O_RDONLY, 0);
-            dfd2 = dup(dfd1);
-            dp = fdopendir(dfd1);
-        } catch (UnixException x) {
-            if (dfd1 != -1)
-                close(dfd1);
-            if (dfd2 != -1)
-                close(dfd2);
-            if (x.errno() == UnixConstants.ENOTDIR)
-                throw new NotDirectoryException(getPathForExecptionMessage());
-            x.rethrowAsIOException(this);
-        }
-        return new UnixSecureDirectoryStream(this, dp, dfd2, filter);
-    }
-
-    // invoked by AbstractPath#copyTo
-    @Override
-    public void implCopyTo(Path obj, CopyOption... options)
-        throws IOException
-    {
-        UnixPath target = (UnixPath)obj;
-        UnixCopyFile.copy(this, target, options);
-    }
-
-    @Override
-    public void implMoveTo(Path obj, CopyOption... options)
-        throws IOException
-    {
-        UnixPath target = (UnixPath)obj;
-        UnixCopyFile.move(this, target, options);
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <V extends FileAttributeView> V
-        getFileAttributeView(Class<V> type, LinkOption... options)
-    {
-        FileAttributeView view = getFileSystem()
-            .newFileAttributeView(type, this, options);
-        if (view == null)
-            return null;
-        return (V) view;
-    }
-
-    @Override
-    public DynamicFileAttributeView getFileAttributeView(String name,
-                                                         LinkOption... options)
-    {
-        return getFileSystem().newFileAttributeView(name, this, options);
-    }
-
-    @Override
-    public Path createDirectory(FileAttribute<?>... attrs)
-        throws IOException
-    {
-        checkWrite();
-
-        int mode = UnixFileModeAttribute
-            .toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs);
-        try {
-            mkdir(this, mode);
-        } catch (UnixException x) {
-            x.rethrowAsIOException(this);
-        }
-        return this;
-    }
-
-    @Override
-    public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
-                                              FileAttribute<?>... attrs)
-         throws IOException
-    {
-        int mode = UnixFileModeAttribute
-            .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
-        try {
-            return UnixChannelFactory.newFileChannel(this, options, mode);
-        } catch (UnixException x) {
-            x.rethrowAsIOException(this);
-            return null;  // keep compiler happy
-        }
-    }
-
-    @Override
-    public boolean isSameFile(Path obj) throws IOException {
-        if (this.equals(obj))
-            return true;
-        if (!(obj instanceof UnixPath))  // includes null check
-            return false;
-        UnixPath other = (UnixPath)obj;
-
-        // check security manager access to both files
-        this.checkRead();
-        other.checkRead();
-
-        UnixFileAttributes thisAttrs;
-        UnixFileAttributes otherAttrs;
-        try {
-             thisAttrs = UnixFileAttributes.get(this, true);
-        } catch (UnixException x) {
-            x.rethrowAsIOException(this);
-            return false;    // keep compiler happy
-        }
-        try {
-            otherAttrs = UnixFileAttributes.get(other, true);
-        } catch (UnixException x) {
-            x.rethrowAsIOException(other);
-            return false;    // keep compiler happy
-        }
-        return thisAttrs.isSameFile(otherAttrs);
-    }
-
-    @Override
-    public Path createSymbolicLink(Path obj, FileAttribute<?>... attrs)
-        throws IOException
-    {
-        UnixPath target = checkPath(obj);
-
-        // no attributes supported when creating links
-        if (attrs.length > 0) {
-            UnixFileModeAttribute.toUnixMode(0, 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"));
-            checkWrite();
-        }
-
-        // create link
-        try {
-            symlink(target.asByteArray(), this);
-        } catch (UnixException x) {
-            x.rethrowAsIOException(this);
-        }
-
-        return this;
-    }
-
-    @Override
-    public Path createLink(Path obj) throws IOException {
-        UnixPath existing = checkPath(obj);
-
-        // permission check
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new LinkPermission("hard"));
-            this.checkWrite();
-            existing.checkWrite();
-        }
-        try {
-            link(existing, this);
-        } catch (UnixException x) {
-            x.rethrowAsIOException(this, existing);
-        }
-        return this;
-    }
-
-    @Override
-    public Path readSymbolicLink() throws IOException {
-        // permission check
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            FilePermission perm = new FilePermission(getPathForPermissionCheck(),
-                SecurityConstants.FILE_READLINK_ACTION);
-            AccessController.checkPermission(perm);
-        }
-        try {
-            byte[] target = readlink(this);
-            return new UnixPath(getFileSystem(), target);
-        } catch (UnixException x) {
-           if (x.errno() == UnixConstants.EINVAL)
-                throw new NotLinkException(getPathForExecptionMessage());
-            x.rethrowAsIOException(this);
-            return null;    // keep compiler happy
-        }
-    }
-
-    @Override
     public UnixPath toAbsolutePath() {
         if (isAbsolute()) {
             return this;
         }
         // The path is relative so need to resolve against default directory,

@@ -1093,11 +813,11 @@
         return new UnixPath(getFileSystem(),
             resolve(getFileSystem().defaultDirectory(), path));
     }
 
     @Override
-    public UnixPath toRealPath(boolean resolveLinks) throws IOException {
+    public Path toRealPath(boolean resolveLinks) throws IOException {
         checkRead();
 
         UnixPath absolute = toAbsolutePath();
 
         // if resolveLinks is true then use realpath

@@ -1110,12 +830,11 @@
             }
         }
 
         // if resolveLinks is false then eliminate "." and also ".."
         // where the previous element is not a link.
-        UnixPath root = getFileSystem().rootDirectory();
-        UnixPath result = root;
+        UnixPath result = fs.rootDirectory();
         for (int i=0; i<absolute.getNameCount(); i++) {
             UnixPath element = absolute.getName(i);
 
             // eliminate "."
             if ((element.asByteArray().length == 1) && (element.asByteArray()[0] == '.'))

@@ -1132,11 +851,11 @@
                     x.rethrowAsIOException(result);
                 }
                 if (!attrs.isSymbolicLink()) {
                     result = result.getParent();
                     if (result == null) {
-                        result = root;
+                        result = fs.rootDirectory();
                     }
                     continue;
                 }
             }
             result = result.resolve(element);

@@ -1149,19 +868,10 @@
             x.rethrowAsIOException(result);
         }
         return result;
     }
 
-    @Override
-    public boolean isHidden() {
-        checkRead();
-        UnixPath name = getName();
-        if (name == null)
-            return false;
-        return (name.asByteArray()[0] == '.');
-    }
-
     @Override
     public URI toUri() {
         return UnixUriUtils.toUri(this);
     }