jdk/src/solaris/classes/sun/nio/fs/UnixPath.java
changeset 2057 3acf8e5e2ca0
child 3065 452aaa2899fc
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.nio.fs;
       
    27 
       
    28 import java.nio.*;
       
    29 import java.nio.file.*;
       
    30 import java.nio.file.attribute.*;
       
    31 import java.nio.file.spi.AbstractPath;
       
    32 import java.nio.charset.*;
       
    33 import java.nio.channels.*;
       
    34 import java.security.AccessController;
       
    35 import java.io.*;
       
    36 import java.net.URI;
       
    37 import java.util.*;
       
    38 import java.lang.ref.SoftReference;
       
    39 import sun.security.util.SecurityConstants;
       
    40 
       
    41 import static sun.nio.fs.UnixNativeDispatcher.*;
       
    42 import static sun.nio.fs.UnixConstants.*;
       
    43 
       
    44 /**
       
    45  * Solaris/Linux implementation of java.nio.file.Path
       
    46  */
       
    47 
       
    48 class UnixPath
       
    49     extends AbstractPath
       
    50 {
       
    51     private static ThreadLocal<SoftReference<CharsetEncoder>> encoder =
       
    52         new ThreadLocal<SoftReference<CharsetEncoder>>();
       
    53 
       
    54     // FIXME - eliminate this reference to reduce space
       
    55     private final UnixFileSystem fs;
       
    56 
       
    57     // internal representation
       
    58     private final byte[] path;
       
    59 
       
    60     // String representation (created lazily)
       
    61     private volatile String stringValue;
       
    62 
       
    63     // cached hashcode (created lazily, no need to be volatile)
       
    64     private int hash;
       
    65 
       
    66     // array of offsets of elements in path (created lazily)
       
    67     private volatile int[] offsets;
       
    68 
       
    69     // file permissions (created lazily)
       
    70     private volatile FilePermission[] perms;
       
    71 
       
    72     UnixPath(UnixFileSystem fs, byte[] path) {
       
    73         this.fs = fs;
       
    74         this.path = path;
       
    75     }
       
    76 
       
    77     UnixPath(UnixFileSystem fs, String input) {
       
    78         // removes redundant slashes and checks for invalid characters
       
    79         this(fs, encode(normalizeAndCheck(input)));
       
    80     }
       
    81 
       
    82     // package-private
       
    83     // removes redundant slashes and check input for invalid characters
       
    84     static String normalizeAndCheck(String input) {
       
    85         int n = input.length();
       
    86         if (n == 0)
       
    87             throw new InvalidPathException(input, "Path is empty");
       
    88         char prevChar = 0;
       
    89         for (int i=0; i < n; i++) {
       
    90             char c = input.charAt(i);
       
    91             if (c == '\u0000')
       
    92                 throw new InvalidPathException(input, "Nul character not allowed");
       
    93             if ((c == '/') && (prevChar == '/'))
       
    94                 return normalize(input, n, i - 1);
       
    95             prevChar = c;
       
    96         }
       
    97         if (prevChar == '/')
       
    98             return normalize(input, n, n - 1);
       
    99         return input;
       
   100     }
       
   101 
       
   102     private static String normalize(String input, int len, int off) {
       
   103         if (len == 0)
       
   104             return input;
       
   105         int n = len;
       
   106         while ((n > 0) && (input.charAt(n - 1) == '/')) n--;
       
   107         if (n == 0)
       
   108             return "/";
       
   109         StringBuilder sb = new StringBuilder(input.length());
       
   110         if (off > 0)
       
   111             sb.append(input.substring(0, off));
       
   112         char prevChar = 0;
       
   113         for (int i=off; i < n; i++) {
       
   114             char c = input.charAt(i);
       
   115             if ((c == '/') && (prevChar == '/'))
       
   116                 continue;
       
   117             sb.append(c);
       
   118             prevChar = c;
       
   119         }
       
   120         return sb.toString();
       
   121     }
       
   122 
       
   123     // encodes the given path-string into a sequence of bytes
       
   124     private static byte[] encode(String input) {
       
   125         SoftReference<CharsetEncoder> ref = encoder.get();
       
   126         CharsetEncoder ce = (ref != null) ? ref.get() : null;
       
   127         if (ce == null) {
       
   128             ce = Charset.defaultCharset().newEncoder()
       
   129                 .onMalformedInput(CodingErrorAction.REPORT)
       
   130                 .onUnmappableCharacter(CodingErrorAction.REPORT);
       
   131             encoder.set(new SoftReference<CharsetEncoder>(ce));
       
   132         }
       
   133 
       
   134         char[] ca = input.toCharArray();
       
   135 
       
   136         // size output buffer for worse-case size
       
   137         byte[] ba = new byte[(int)(ca.length * (double)ce.maxBytesPerChar())];
       
   138 
       
   139         // encode
       
   140         ByteBuffer bb = ByteBuffer.wrap(ba);
       
   141         CharBuffer cb = CharBuffer.wrap(ca);
       
   142         ce.reset();
       
   143         CoderResult cr = ce.encode(cb, bb, true);
       
   144         boolean error;
       
   145         if (!cr.isUnderflow()) {
       
   146             error = true;
       
   147         } else {
       
   148             cr = ce.flush(bb);
       
   149             error = !cr.isUnderflow();
       
   150         }
       
   151         if (error) {
       
   152             throw new InvalidPathException(input,
       
   153                 "Malformed input or input contains unmappable chacraters");
       
   154         }
       
   155 
       
   156         // trim result to actual length if required
       
   157         int len = bb.position();
       
   158         if (len != ba.length)
       
   159             ba = Arrays.copyOf(ba, len);
       
   160 
       
   161         return ba;
       
   162     }
       
   163 
       
   164     // package-private
       
   165     byte[] asByteArray() {
       
   166         return path;
       
   167     }
       
   168 
       
   169     // use this path when making system/library calls
       
   170     byte[] getByteArrayForSysCalls() {
       
   171         // resolve against default directory if required (chdir allowed or
       
   172         // file system default directory is not working directory)
       
   173         if (getFileSystem().needToResolveAgainstDefaultDirectory()) {
       
   174             return resolve(getFileSystem().defaultDirectory(), path);
       
   175         } else {
       
   176             return path;
       
   177         }
       
   178     }
       
   179 
       
   180     // use this message when throwing exceptions
       
   181     String getPathForExecptionMessage() {
       
   182         return toString();
       
   183     }
       
   184 
       
   185     // use this path for permission checks
       
   186     String getPathForPermissionCheck() {
       
   187         if (getFileSystem().needToResolveAgainstDefaultDirectory()) {
       
   188             return new String(getByteArrayForSysCalls());
       
   189         } else {
       
   190             return toString();
       
   191         }
       
   192     }
       
   193 
       
   194     // Checks that the given file is a UnixPath
       
   195     private UnixPath checkPath(FileRef obj) {
       
   196         if (obj == null)
       
   197             throw new NullPointerException();
       
   198         if (!(obj instanceof UnixPath))
       
   199             throw new ProviderMismatchException();
       
   200         return (UnixPath)obj;
       
   201     }
       
   202 
       
   203     // create offset list if not already created
       
   204     private void initOffsets() {
       
   205         if (offsets == null) {
       
   206             int count, index;
       
   207 
       
   208             // count names
       
   209             count = 0;
       
   210             index = 0;
       
   211             while (index < path.length) {
       
   212                 byte c = path[index++];
       
   213                 if (c != '/') {
       
   214                     count++;
       
   215                     while (index < path.length && path[index] != '/')
       
   216                         index++;
       
   217                 }
       
   218             }
       
   219 
       
   220             // populate offsets
       
   221             int[] result = new int[count];
       
   222             count = 0;
       
   223             index = 0;
       
   224             while (index < path.length) {
       
   225                 byte c = path[index];
       
   226                 if (c == '/') {
       
   227                     index++;
       
   228                 } else {
       
   229                     result[count++] = index++;
       
   230                     while (index < path.length && path[index] != '/')
       
   231                         index++;
       
   232                 }
       
   233             }
       
   234             synchronized (this) {
       
   235                 if (offsets == null)
       
   236                     offsets = result;
       
   237             }
       
   238         }
       
   239     }
       
   240 
       
   241     @Override
       
   242     public UnixFileSystem getFileSystem() {
       
   243         return fs;
       
   244     }
       
   245 
       
   246     @Override
       
   247     public UnixPath getRoot() {
       
   248         if (path[0] == '/') {
       
   249             return getFileSystem().rootDirectory();
       
   250         } else {
       
   251             return null;
       
   252         }
       
   253     }
       
   254 
       
   255     @Override
       
   256     public UnixPath getName() {
       
   257         initOffsets();
       
   258 
       
   259         int count = offsets.length;
       
   260         if (count == 0)
       
   261             return null;  // no elements so no name
       
   262 
       
   263         if (count == 1 && path[0] != '/')
       
   264             return this;
       
   265 
       
   266         int lastOffset = offsets[count-1];
       
   267         int len = path.length - lastOffset;
       
   268         byte[] result = new byte[len];
       
   269         System.arraycopy(path, lastOffset, result, 0, len);
       
   270         return new UnixPath(getFileSystem(), result);
       
   271     }
       
   272 
       
   273     @Override
       
   274     public UnixPath getParent() {
       
   275         initOffsets();
       
   276 
       
   277         int count = offsets.length;
       
   278         if (count == 0) {
       
   279             // no elements so no parent
       
   280             return null;
       
   281         }
       
   282         int len = offsets[count-1] - 1;
       
   283         if (len <= 0) {
       
   284             // parent is root only (may be null)
       
   285             return getRoot();
       
   286         }
       
   287         byte[] result = new byte[len];
       
   288         System.arraycopy(path, 0, result, 0, len);
       
   289         return new UnixPath(getFileSystem(), result);
       
   290     }
       
   291 
       
   292     @Override
       
   293     public int getNameCount() {
       
   294         initOffsets();
       
   295         return offsets.length;
       
   296     }
       
   297 
       
   298     @Override
       
   299     public UnixPath getName(int index) {
       
   300         initOffsets();
       
   301         if (index < 0)
       
   302             throw new IllegalArgumentException();
       
   303         if (index >= offsets.length)
       
   304             throw new IllegalArgumentException();
       
   305 
       
   306         int begin = offsets[index];
       
   307         int len;
       
   308         if (index == (offsets.length-1)) {
       
   309             len = path.length - begin;
       
   310         } else {
       
   311             len = offsets[index+1] - begin - 1;
       
   312         }
       
   313 
       
   314         // construct result
       
   315         byte[] result = new byte[len];
       
   316         System.arraycopy(path, begin, result, 0, len);
       
   317         return new UnixPath(getFileSystem(), result);
       
   318     }
       
   319 
       
   320     @Override
       
   321     public UnixPath subpath(int beginIndex, int endIndex) {
       
   322         initOffsets();
       
   323 
       
   324         if (beginIndex < 0)
       
   325             throw new IllegalArgumentException();
       
   326         if (beginIndex >= offsets.length)
       
   327             throw new IllegalArgumentException();
       
   328         if (endIndex > offsets.length)
       
   329             throw new IllegalArgumentException();
       
   330         if (beginIndex >= endIndex) {
       
   331             throw new IllegalArgumentException();
       
   332         }
       
   333 
       
   334         // starting offset and length
       
   335         int begin = offsets[beginIndex];
       
   336         int len;
       
   337         if (endIndex == offsets.length) {
       
   338             len = path.length - begin;
       
   339         } else {
       
   340             len = offsets[endIndex] - begin - 1;
       
   341         }
       
   342 
       
   343         // construct result
       
   344         byte[] result = new byte[len];
       
   345         System.arraycopy(path, begin, result, 0, len);
       
   346         return new UnixPath(getFileSystem(), result);
       
   347     }
       
   348 
       
   349     @Override
       
   350     public boolean isAbsolute() {
       
   351         return (path[0] == '/');
       
   352     }
       
   353 
       
   354     // Resolve child against given base
       
   355     private static byte[] resolve(byte[] base, byte[] child) {
       
   356         if (child[0] == '/')
       
   357             return child;
       
   358         byte[] result;
       
   359         if (base.length == 1 && base[0] == '/') {
       
   360             result = new byte[child.length + 1];
       
   361             result[0] = '/';
       
   362             System.arraycopy(child, 0, result, 1, child.length);
       
   363         } else {
       
   364             result = new byte[base.length + 1 + child.length];
       
   365             System.arraycopy(base, 0, result, 0, base.length);
       
   366             result[base.length] = '/';
       
   367             System.arraycopy(child, 0, result,  base.length+1, child.length);
       
   368         }
       
   369         return result;
       
   370     }
       
   371 
       
   372     @Override
       
   373     public UnixPath resolve(Path obj) {
       
   374         if (obj == null)
       
   375             return this;
       
   376         byte[] other = checkPath(obj).path;
       
   377         if (other[0] == '/')
       
   378             return ((UnixPath)obj);
       
   379         byte[] result = resolve(path, other);
       
   380         return new UnixPath(getFileSystem(), result);
       
   381     }
       
   382 
       
   383     @Override
       
   384     public UnixPath resolve(String other) {
       
   385         return resolve(new UnixPath(getFileSystem(), other));
       
   386     }
       
   387 
       
   388     UnixPath resolve(byte[] other) {
       
   389         return resolve(new UnixPath(getFileSystem(), other));
       
   390     }
       
   391 
       
   392     @Override
       
   393     public UnixPath relativize(Path obj) {
       
   394         UnixPath other = checkPath(obj);
       
   395         if (other.equals(this))
       
   396             return null;
       
   397 
       
   398         // can only relativize paths of the same type
       
   399         if (this.isAbsolute() != other.isAbsolute())
       
   400             throw new IllegalArgumentException("'other' is different type of Path");
       
   401 
       
   402         int bn = this.getNameCount();
       
   403         int cn = other.getNameCount();
       
   404 
       
   405         // skip matching names
       
   406         int n = (bn > cn) ? cn : bn;
       
   407         int i = 0;
       
   408         while (i < n) {
       
   409             if (!this.getName(i).equals(other.getName(i)))
       
   410                 break;
       
   411             i++;
       
   412         }
       
   413 
       
   414         int dotdots = bn - i;
       
   415         if (i < cn) {
       
   416             // remaining name components in other
       
   417             UnixPath remainder = other.subpath(i, cn);
       
   418             if (dotdots == 0)
       
   419                 return remainder;
       
   420 
       
   421             // result is a  "../" for each remaining name in base
       
   422             // followed by the remaining names in other
       
   423             byte[] result = new byte[dotdots*3 + remainder.path.length];
       
   424             int pos = 0;
       
   425             while (dotdots > 0) {
       
   426                 result[pos++] = (byte)'.';
       
   427                 result[pos++] = (byte)'.';
       
   428                 result[pos++] = (byte)'/';
       
   429                 dotdots--;
       
   430             }
       
   431             System.arraycopy(remainder.path, 0, result, pos, remainder.path.length);
       
   432             return new UnixPath(getFileSystem(), result);
       
   433         } else {
       
   434             // no remaining names in other so result is simply a sequence of ".."
       
   435             byte[] result = new byte[dotdots*3 - 1];
       
   436             int pos = 0;
       
   437             while (dotdots > 0) {
       
   438                 result[pos++] = (byte)'.';
       
   439                 result[pos++] = (byte)'.';
       
   440                 // no tailing slash at the end
       
   441                 if (dotdots > 1)
       
   442                     result[pos++] = (byte)'/';
       
   443                 dotdots--;
       
   444             }
       
   445             return new UnixPath(getFileSystem(), result);
       
   446         }
       
   447     }
       
   448 
       
   449     @Override
       
   450     public Path normalize() {
       
   451         final int count = getNameCount();
       
   452         if (count == 0)
       
   453             return this;
       
   454 
       
   455         boolean[] ignore = new boolean[count];      // true => ignore name
       
   456         int[] size = new int[count];                // length of name
       
   457         int remaining = count;                      // number of names remaining
       
   458         boolean hasDotDot = false;                  // has at least one ..
       
   459         boolean isAbsolute = path[0] == '/';
       
   460 
       
   461         // first pass:
       
   462         //   1. compute length of names
       
   463         //   2. mark all occurences of "." to ignore
       
   464         //   3. and look for any occurences of ".."
       
   465         for (int i=0; i<count; i++) {
       
   466             int begin = offsets[i];
       
   467             int len;
       
   468             if (i == (offsets.length-1)) {
       
   469                 len = path.length - begin;
       
   470             } else {
       
   471                 len = offsets[i+1] - begin - 1;
       
   472             }
       
   473             size[i] = len;
       
   474 
       
   475             if (path[begin] == '.') {
       
   476                 if (len == 1) {
       
   477                     ignore[i] = true;  // ignore  "."
       
   478                     remaining--;
       
   479                 }
       
   480                 else {
       
   481                     if (path[begin+1] == '.')   // ".." found
       
   482                         hasDotDot = true;
       
   483                 }
       
   484             }
       
   485         }
       
   486 
       
   487         // multiple passes to eliminate all occurences of name/..
       
   488         if (hasDotDot) {
       
   489             int prevRemaining;
       
   490             do {
       
   491                 prevRemaining = remaining;
       
   492                 int prevName = -1;
       
   493                 for (int i=0; i<count; i++) {
       
   494                     if (ignore[i])
       
   495                         continue;
       
   496 
       
   497                     // not a ".."
       
   498                     if (size[i] != 2) {
       
   499                         prevName = i;
       
   500                         continue;
       
   501                     }
       
   502 
       
   503                     int begin = offsets[i];
       
   504                     if (path[begin] != '.' || path[begin+1] != '.') {
       
   505                         prevName = i;
       
   506                         continue;
       
   507                     }
       
   508 
       
   509                     // ".." found
       
   510                     if (prevName >= 0) {
       
   511                         // name/<ignored>/.. found so mark name and ".." to be
       
   512                         // ignored
       
   513                         ignore[prevName] = true;
       
   514                         ignore[i] = true;
       
   515                         remaining = remaining - 2;
       
   516                         prevName = -1;
       
   517                     } else {
       
   518                         // Case: /<ignored>/.. so mark ".." as ignored
       
   519                         if (isAbsolute) {
       
   520                             boolean hasPrevious = false;
       
   521                             for (int j=0; j<i; j++) {
       
   522                                 if (!ignore[j]) {
       
   523                                     hasPrevious = true;
       
   524                                     break;
       
   525                                 }
       
   526                             }
       
   527                             if (!hasPrevious) {
       
   528                                 // all proceeding names are ignored
       
   529                                 ignore[i] = true;
       
   530                                 remaining--;
       
   531                             }
       
   532                         }
       
   533                     }
       
   534                 }
       
   535             } while (prevRemaining > remaining);
       
   536         }
       
   537 
       
   538         // no redundant names
       
   539         if (remaining == count)
       
   540             return this;
       
   541 
       
   542         // corner case - all names removed
       
   543         if (remaining == 0) {
       
   544             return isAbsolute ? getFileSystem().rootDirectory() : null;
       
   545         }
       
   546 
       
   547         // compute length of result
       
   548         int len = remaining - 1;
       
   549         if (isAbsolute)
       
   550             len++;
       
   551 
       
   552         for (int i=0; i<count; i++) {
       
   553             if (!ignore[i])
       
   554                 len += size[i];
       
   555         }
       
   556         byte[] result = new byte[len];
       
   557 
       
   558         // copy names into result
       
   559         int pos = 0;
       
   560         if (isAbsolute)
       
   561             result[pos++] = '/';
       
   562         for (int i=0; i<count; i++) {
       
   563             if (!ignore[i]) {
       
   564                 System.arraycopy(path, offsets[i], result, pos, size[i]);
       
   565                 pos += size[i];
       
   566                 if (--remaining > 0) {
       
   567                     result[pos++] = '/';
       
   568                 }
       
   569             }
       
   570         }
       
   571         return new UnixPath(getFileSystem(), result);
       
   572     }
       
   573 
       
   574     @Override
       
   575     public boolean startsWith(Path other) {
       
   576         UnixPath that = checkPath(other);
       
   577 
       
   578         // other path is longer
       
   579         if (that.path.length > path.length)
       
   580             return false;
       
   581 
       
   582         int thisOffsetCount = getNameCount();
       
   583         int thatOffsetCount = that.getNameCount();
       
   584 
       
   585         // other path has no name elements
       
   586         if (thatOffsetCount == 0 && this.isAbsolute())
       
   587             return true;
       
   588 
       
   589         // given path has more elements that this path
       
   590         if (thatOffsetCount > thisOffsetCount)
       
   591             return false;
       
   592 
       
   593         // same number of elements so must be exact match
       
   594         if ((thatOffsetCount == thisOffsetCount) &&
       
   595             (path.length != that.path.length)) {
       
   596             return false;
       
   597         }
       
   598 
       
   599         // check offsets of elements match
       
   600         for (int i=0; i<thatOffsetCount; i++) {
       
   601             Integer o1 = offsets[i];
       
   602             Integer o2 = that.offsets[i];
       
   603             if (!o1.equals(o2))
       
   604                 return false;
       
   605         }
       
   606 
       
   607         // offsets match so need to compare bytes
       
   608         int i=0;
       
   609         while (i < that.path.length) {
       
   610             if (this.path[i] != that.path[i])
       
   611                 return false;
       
   612             i++;
       
   613         }
       
   614 
       
   615         // final check that match is on name boundary
       
   616         if (i < path.length && this.path[i] != '/')
       
   617             return false;
       
   618 
       
   619         return true;
       
   620     }
       
   621 
       
   622     @Override
       
   623     public boolean endsWith(Path other) {
       
   624         UnixPath that = checkPath(other);
       
   625 
       
   626         // other path is longer
       
   627         if (that.path.length > path.length)
       
   628             return false;
       
   629 
       
   630         // other path is absolute so this path must be absolute
       
   631         if (that.isAbsolute() && !this.isAbsolute())
       
   632             return false;
       
   633 
       
   634         int thisOffsetCount = getNameCount();
       
   635         int thatOffsetCount = that.getNameCount();
       
   636 
       
   637         // given path has more elements that this path
       
   638         if (thatOffsetCount > thisOffsetCount) {
       
   639             return false;
       
   640         } else {
       
   641             // same number of elements
       
   642             if (thatOffsetCount == thisOffsetCount) {
       
   643                 if (thisOffsetCount == 0)
       
   644                     return true;
       
   645                 int expectedLen = path.length;
       
   646                 if (this.isAbsolute() && !that.isAbsolute())
       
   647                     expectedLen--;
       
   648                 if (that.path.length != expectedLen)
       
   649                     return false;
       
   650             } else {
       
   651                 // this path has more elements so given path must be relative
       
   652                 if (that.isAbsolute())
       
   653                     return false;
       
   654             }
       
   655         }
       
   656 
       
   657         // compare bytes
       
   658         int thisPos = offsets[thisOffsetCount - thatOffsetCount];
       
   659         int thatPos = that.offsets[0];
       
   660         while (thatPos < that.path.length) {
       
   661             if (this.path[thisPos++] != that.path[thatPos++])
       
   662                 return false;
       
   663         }
       
   664 
       
   665         return true;
       
   666     }
       
   667 
       
   668     @Override
       
   669     public int compareTo(Path other) {
       
   670         int len1 = path.length;
       
   671         int len2 = ((UnixPath) other).path.length;
       
   672 
       
   673         int n = Math.min(len1, len2);
       
   674         byte v1[] = path;
       
   675         byte v2[] = ((UnixPath) other).path;
       
   676 
       
   677         int k = 0;
       
   678         while (k < n) {
       
   679             int c1 = v1[k] & 0xff;
       
   680             int c2 = v2[k] & 0xff;
       
   681             if (c1 != c2) {
       
   682                 return c1 - c2;
       
   683             }
       
   684             k++;
       
   685         }
       
   686         return len1 - len2;
       
   687     }
       
   688 
       
   689     @Override
       
   690     public boolean equals(Object ob) {
       
   691         if ((ob != null) && (ob instanceof UnixPath)) {
       
   692             return compareTo((Path)ob) == 0;
       
   693         }
       
   694         return false;
       
   695     }
       
   696 
       
   697     @Override
       
   698     public int hashCode() {
       
   699         // OK if two or more threads compute hash
       
   700         int h = hash;
       
   701         if (h == 0) {
       
   702             for (int i = 0; i< path.length; i++) {
       
   703                 h = 31*h + (path[i] & 0xff);
       
   704             }
       
   705             hash = h;
       
   706         }
       
   707         return h;
       
   708     }
       
   709 
       
   710     @Override
       
   711     public String toString() {
       
   712         // OK if two or more threads create a String
       
   713         if (stringValue == null)
       
   714             stringValue = new String(path);     // platform encoding
       
   715         return stringValue;
       
   716     }
       
   717 
       
   718     @Override
       
   719     public Iterator<Path> iterator() {
       
   720         initOffsets();
       
   721         return new Iterator<Path>() {
       
   722             int i = 0;
       
   723             @Override
       
   724             public boolean hasNext() {
       
   725                 return (i < offsets.length);
       
   726             }
       
   727             @Override
       
   728             public Path next() {
       
   729                 if (i < offsets.length) {
       
   730                     Path result = getName(i);
       
   731                     i++;
       
   732                     return result;
       
   733                 } else {
       
   734                     throw new NoSuchElementException();
       
   735                 }
       
   736             }
       
   737             @Override
       
   738             public void remove() {
       
   739                 throw new UnsupportedOperationException();
       
   740             }
       
   741         };
       
   742     }
       
   743 
       
   744     // -- file operations --
       
   745 
       
   746     // package-private
       
   747     int openForAttributeAccess(boolean followLinks) throws IOException {
       
   748         int flags = O_RDONLY;
       
   749         if (!followLinks)
       
   750             flags |= O_NOFOLLOW;
       
   751         try {
       
   752             return open(this, flags, 0);
       
   753         } catch (UnixException x) {
       
   754             // HACK: EINVAL instead of ELOOP on Solaris 10 prior to u4 (see 6460380)
       
   755             if (getFileSystem().isSolaris() && x.errno() == EINVAL)
       
   756                 x.setError(ELOOP);
       
   757 
       
   758             if (x.errno() == ELOOP)
       
   759                 throw new FileSystemException(getPathForExecptionMessage(), null,
       
   760                     x.getMessage() + " or unable to access attributes of symbolic link");
       
   761 
       
   762             x.rethrowAsIOException(this);
       
   763             return -1; // keep compile happy
       
   764         }
       
   765     }
       
   766 
       
   767     // create file permissions used for read and write checks
       
   768     private void checkReadOrWrite(boolean checkRead) {
       
   769         SecurityManager sm = System.getSecurityManager();
       
   770         if (sm == null)
       
   771             return;
       
   772         if (perms == null) {
       
   773             synchronized (this) {
       
   774                 if (perms == null) {
       
   775                     FilePermission[] p = new FilePermission[2];
       
   776                     String pathForPermCheck = getPathForPermissionCheck();
       
   777                     p[0] = new FilePermission(pathForPermCheck,
       
   778                         SecurityConstants.FILE_READ_ACTION);
       
   779                     p[1] = new FilePermission(pathForPermCheck,
       
   780                         SecurityConstants.FILE_WRITE_ACTION);
       
   781                     perms = p;
       
   782                 }
       
   783             }
       
   784         }
       
   785         if (checkRead) {
       
   786             sm.checkPermission(perms[0]);
       
   787         } else {
       
   788             sm.checkPermission(perms[1]);
       
   789         }
       
   790     }
       
   791 
       
   792     void checkRead() {
       
   793         checkReadOrWrite(true);
       
   794     }
       
   795 
       
   796     void checkWrite() {
       
   797         checkReadOrWrite(false);
       
   798     }
       
   799 
       
   800     void checkDelete() {
       
   801         SecurityManager sm = System.getSecurityManager();
       
   802         if (sm != null) {
       
   803             // permission not cached
       
   804             sm.checkDelete(getPathForPermissionCheck());
       
   805         }
       
   806     }
       
   807 
       
   808     @Override
       
   809     public FileStore getFileStore()
       
   810         throws IOException
       
   811     {
       
   812         SecurityManager sm = System.getSecurityManager();
       
   813         if (sm != null) {
       
   814             sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
       
   815             checkRead();
       
   816         }
       
   817         return getFileSystem().getFileStore(this);
       
   818     }
       
   819 
       
   820     @Override
       
   821     public void checkAccess(AccessMode... modes) throws IOException {
       
   822         boolean e = false;
       
   823         boolean r = false;
       
   824         boolean w = false;
       
   825         boolean x = false;
       
   826 
       
   827         if (modes.length == 0) {
       
   828             e = true;
       
   829         } else {
       
   830             for (AccessMode mode: modes) {
       
   831                 switch (mode) {
       
   832                     case READ : r = true; break;
       
   833                     case WRITE : w = true; break;
       
   834                     case EXECUTE : x = true; break;
       
   835                     default: throw new AssertionError("Should not get here");
       
   836                 }
       
   837             }
       
   838         }
       
   839 
       
   840         int mode = 0;
       
   841         if (e || r) {
       
   842             checkRead();
       
   843             mode |= (r) ? R_OK : F_OK;
       
   844         }
       
   845         if (w) {
       
   846             checkWrite();
       
   847             mode |= W_OK;
       
   848         }
       
   849         if (x) {
       
   850             SecurityManager sm = System.getSecurityManager();
       
   851             if (sm != null) {
       
   852                 // not cached
       
   853                 sm.checkExec(getPathForPermissionCheck());
       
   854             }
       
   855             mode |= X_OK;
       
   856         }
       
   857         try {
       
   858             access(this, mode);
       
   859         } catch (UnixException exc) {
       
   860             exc.rethrowAsIOException(this);
       
   861         }
       
   862     }
       
   863 
       
   864     @Override
       
   865     public void delete(boolean failIfNotExists) throws IOException {
       
   866         checkDelete();
       
   867 
       
   868         // need file attributes to know if file is directory
       
   869         UnixFileAttributes attrs = null;
       
   870         try {
       
   871             attrs = UnixFileAttributes.get(this, false);
       
   872             if (attrs.isDirectory()) {
       
   873                 rmdir(this);
       
   874             } else {
       
   875                 unlink(this);
       
   876             }
       
   877         } catch (UnixException x) {
       
   878             // no-op if file does not exist
       
   879             if (!failIfNotExists && x.errno() == ENOENT)
       
   880                 return;
       
   881 
       
   882             // DirectoryNotEmptyException if not empty
       
   883             if (attrs != null && attrs.isDirectory() &&
       
   884                 (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
       
   885                 throw new DirectoryNotEmptyException(getPathForExecptionMessage());
       
   886 
       
   887             x.rethrowAsIOException(this);
       
   888         }
       
   889     }
       
   890 
       
   891     @Override
       
   892     public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter)
       
   893         throws IOException
       
   894     {
       
   895         if (filter == null)
       
   896             throw new NullPointerException();
       
   897         checkRead();
       
   898 
       
   899         // can't return SecureDirectoryStream on older kernels.
       
   900         if (!getFileSystem().supportsSecureDirectoryStreams()) {
       
   901             try {
       
   902                 long ptr = opendir(this);
       
   903                 return new UnixDirectoryStream(this, ptr, filter);
       
   904             } catch (UnixException x) {
       
   905                 if (x.errno() == UnixConstants.ENOTDIR)
       
   906                     throw new NotDirectoryException(getPathForExecptionMessage());
       
   907                 x.rethrowAsIOException(this);
       
   908             }
       
   909         }
       
   910 
       
   911         // open directory and dup file descriptor for use by
       
   912         // opendir/readdir/closedir
       
   913         int dfd1 = -1;
       
   914         int dfd2 = -1;
       
   915         long dp = 0L;
       
   916         try {
       
   917             dfd1 = open(this, O_RDONLY, 0);
       
   918             dfd2 = dup(dfd1);
       
   919             dp = fdopendir(dfd1);
       
   920         } catch (UnixException x) {
       
   921             if (dfd1 != -1)
       
   922                 close(dfd1);
       
   923             if (dfd2 != -1)
       
   924                 close(dfd2);
       
   925             if (x.errno() == UnixConstants.ENOTDIR)
       
   926                 throw new NotDirectoryException(getPathForExecptionMessage());
       
   927             x.rethrowAsIOException(this);
       
   928         }
       
   929         return new UnixSecureDirectoryStream(this, dp, dfd2, filter);
       
   930     }
       
   931 
       
   932     // invoked by AbstractPath#copyTo
       
   933     @Override
       
   934     public void implCopyTo(Path obj, CopyOption... options)
       
   935         throws IOException
       
   936     {
       
   937         UnixPath target = (UnixPath)obj;
       
   938         UnixCopyFile.copy(this, target, options);
       
   939     }
       
   940 
       
   941     @Override
       
   942     public void implMoveTo(Path obj, CopyOption... options)
       
   943         throws IOException
       
   944     {
       
   945         UnixPath target = (UnixPath)obj;
       
   946         UnixCopyFile.move(this, target, options);
       
   947     }
       
   948 
       
   949     @Override
       
   950     @SuppressWarnings("unchecked")
       
   951     public <V extends FileAttributeView> V
       
   952         getFileAttributeView(Class<V> type, LinkOption... options)
       
   953     {
       
   954         FileAttributeView view = getFileSystem()
       
   955             .newFileAttributeView(type, this, options);
       
   956         if (view == null)
       
   957             return null;
       
   958         return (V) view;
       
   959     }
       
   960 
       
   961     @Override
       
   962     public FileAttributeView getFileAttributeView(String name, LinkOption... options) {
       
   963         return getFileSystem().newFileAttributeView(name, this, options);
       
   964     }
       
   965 
       
   966     @Override
       
   967     public Path createDirectory(FileAttribute<?>... attrs)
       
   968         throws IOException
       
   969     {
       
   970         checkWrite();
       
   971 
       
   972         int mode = UnixFileModeAttribute
       
   973             .toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs);
       
   974         try {
       
   975             mkdir(this, mode);
       
   976         } catch (UnixException x) {
       
   977             x.rethrowAsIOException(this);
       
   978         }
       
   979         return this;
       
   980     }
       
   981 
       
   982     @Override
       
   983     public InputStream newInputStream()throws IOException {
       
   984         try {
       
   985             Set<OpenOption> options = Collections.emptySet();
       
   986             FileChannel fc = UnixChannelFactory.newFileChannel(this, options, 0);
       
   987             return Channels.newInputStream(fc);
       
   988         } catch (UnixException x) {
       
   989             x.rethrowAsIOException(this);
       
   990             return null;  // keep compiler happy
       
   991         }
       
   992     }
       
   993 
       
   994     @Override
       
   995     public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
       
   996                                                       FileAttribute<?>... attrs)
       
   997          throws IOException
       
   998     {
       
   999         int mode = UnixFileModeAttribute
       
  1000             .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
       
  1001         try {
       
  1002             return UnixChannelFactory.newFileChannel(this, options, mode);
       
  1003         } catch (UnixException x) {
       
  1004             x.rethrowAsIOException(this);
       
  1005             return null;  // keep compiler happy
       
  1006         }
       
  1007     }
       
  1008 
       
  1009     @Override
       
  1010     public OutputStream newOutputStream(Set<? extends OpenOption> options,
       
  1011                                         FileAttribute<?>... attrs)
       
  1012         throws IOException
       
  1013     {
       
  1014         // need to copy options to add WRITE
       
  1015         Set<OpenOption> opts = new HashSet<OpenOption>(options);
       
  1016         if (opts.contains(StandardOpenOption.READ))
       
  1017             throw new IllegalArgumentException("READ not allowed");
       
  1018         opts.add(StandardOpenOption.WRITE);
       
  1019 
       
  1020         int mode = UnixFileModeAttribute
       
  1021             .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
       
  1022         try {
       
  1023             FileChannel fc = UnixChannelFactory.newFileChannel(this, opts, mode);
       
  1024             return Channels.newOutputStream(fc);
       
  1025         } catch (UnixException x) {
       
  1026             x.rethrowAsIOException(this);
       
  1027             return null;  // keep compiler happy
       
  1028         }
       
  1029     }
       
  1030 
       
  1031     @Override
       
  1032     public boolean isSameFile(FileRef obj) throws IOException {
       
  1033         if (this.equals(obj))
       
  1034             return true;
       
  1035         if (!(obj instanceof UnixPath))  // includes null check
       
  1036             return false;
       
  1037         UnixPath other = (UnixPath)obj;
       
  1038 
       
  1039         // check security manager access to both files
       
  1040         this.checkRead();
       
  1041         other.checkRead();
       
  1042 
       
  1043         UnixFileAttributes thisAttrs;
       
  1044         UnixFileAttributes otherAttrs;
       
  1045         try {
       
  1046              thisAttrs = UnixFileAttributes.get(this, true);
       
  1047         } catch (UnixException x) {
       
  1048             x.rethrowAsIOException(this);
       
  1049             return false;    // keep compiler happy
       
  1050         }
       
  1051         try {
       
  1052             otherAttrs = UnixFileAttributes.get(other, true);
       
  1053         } catch (UnixException x) {
       
  1054             x.rethrowAsIOException(other);
       
  1055             return false;    // keep compiler happy
       
  1056         }
       
  1057         return thisAttrs.isSameFile(otherAttrs);
       
  1058     }
       
  1059 
       
  1060     @Override
       
  1061     public Path createSymbolicLink(Path obj, FileAttribute<?>... attrs)
       
  1062         throws IOException
       
  1063     {
       
  1064         UnixPath target = checkPath(obj);
       
  1065 
       
  1066         // no attributes supported when creating links
       
  1067         if (attrs.length > 0) {
       
  1068             UnixFileModeAttribute.toUnixMode(0, attrs);  // may throw NPE or UOE
       
  1069             throw new UnsupportedOperationException("Initial file attributes" +
       
  1070                 "not supported when creating symbolic link");
       
  1071         }
       
  1072 
       
  1073         // permission check
       
  1074         SecurityManager sm = System.getSecurityManager();
       
  1075         if (sm != null) {
       
  1076             sm.checkPermission(new LinkPermission("symbolic"));
       
  1077             checkWrite();
       
  1078         }
       
  1079 
       
  1080         // create link
       
  1081         try {
       
  1082             symlink(target.asByteArray(), this);
       
  1083         } catch (UnixException x) {
       
  1084             x.rethrowAsIOException(this);
       
  1085         }
       
  1086 
       
  1087         return this;
       
  1088     }
       
  1089 
       
  1090     @Override
       
  1091     public Path createLink(Path obj) throws IOException {
       
  1092         UnixPath existing = checkPath(obj);
       
  1093 
       
  1094         // permission check
       
  1095         SecurityManager sm = System.getSecurityManager();
       
  1096         if (sm != null) {
       
  1097             sm.checkPermission(new LinkPermission("hard"));
       
  1098             this.checkWrite();
       
  1099             existing.checkWrite();
       
  1100         }
       
  1101         try {
       
  1102             link(existing, this);
       
  1103         } catch (UnixException x) {
       
  1104             x.rethrowAsIOException(this, existing);
       
  1105         }
       
  1106         return this;
       
  1107     }
       
  1108 
       
  1109     @Override
       
  1110     public Path readSymbolicLink() throws IOException {
       
  1111         // permission check
       
  1112         SecurityManager sm = System.getSecurityManager();
       
  1113         if (sm != null) {
       
  1114             FilePermission perm = new FilePermission(getPathForPermissionCheck(),
       
  1115                 SecurityConstants.FILE_READLINK_ACTION);
       
  1116             AccessController.checkPermission(perm);
       
  1117         }
       
  1118         try {
       
  1119             byte[] target = readlink(this);
       
  1120             return new UnixPath(getFileSystem(), target);
       
  1121         } catch (UnixException x) {
       
  1122            if (x.errno() == UnixConstants.EINVAL)
       
  1123                 throw new NotLinkException(getPathForExecptionMessage());
       
  1124             x.rethrowAsIOException(this);
       
  1125             return null;    // keep compiler happy
       
  1126         }
       
  1127     }
       
  1128 
       
  1129     @Override
       
  1130     public UnixPath toAbsolutePath() {
       
  1131         if (isAbsolute()) {
       
  1132             return this;
       
  1133         }
       
  1134         // The path is relative so need to resolve against default directory,
       
  1135         // taking care not to reveal the user.dir
       
  1136         SecurityManager sm = System.getSecurityManager();
       
  1137         if (sm != null) {
       
  1138             sm.checkPropertyAccess("user.dir");
       
  1139         }
       
  1140         return new UnixPath(getFileSystem(),
       
  1141             resolve(getFileSystem().defaultDirectory(), path));
       
  1142     }
       
  1143 
       
  1144     @Override
       
  1145     public UnixPath toRealPath(boolean resolveLinks) throws IOException {
       
  1146         checkRead();
       
  1147 
       
  1148         UnixPath absolute = toAbsolutePath();
       
  1149 
       
  1150         // if resolveLinks is true then use realpath
       
  1151         if (resolveLinks) {
       
  1152             try {
       
  1153                 byte[] rp = realpath(absolute);
       
  1154                 return new UnixPath(getFileSystem(), rp);
       
  1155             } catch (UnixException x) {
       
  1156                 x.rethrowAsIOException(this);
       
  1157             }
       
  1158         }
       
  1159 
       
  1160         // if resolveLinks is false then eliminate "." and also ".."
       
  1161         // where the previous element is not a link.
       
  1162         UnixPath root = getFileSystem().rootDirectory();
       
  1163         UnixPath result = root;
       
  1164         for (int i=0; i<absolute.getNameCount(); i++) {
       
  1165             UnixPath element = absolute.getName(i);
       
  1166 
       
  1167             // eliminate "."
       
  1168             if ((element.asByteArray().length == 1) && (element.asByteArray()[0] == '.'))
       
  1169                 continue;
       
  1170 
       
  1171             // cannot eliminate ".." if previous element is a link
       
  1172             if ((element.asByteArray().length == 2) && (element.asByteArray()[0] == '.') &&
       
  1173                 (element.asByteArray()[1] == '.'))
       
  1174             {
       
  1175                 UnixFileAttributes attrs = null;
       
  1176                 try {
       
  1177                     attrs = UnixFileAttributes.get(result, false);
       
  1178                 } catch (UnixException x) {
       
  1179                     x.rethrowAsIOException(result);
       
  1180                 }
       
  1181                 if (!attrs.isSymbolicLink()) {
       
  1182                     result = result.getParent();
       
  1183                     if (result == null) {
       
  1184                         result = root;
       
  1185                     }
       
  1186                     continue;
       
  1187                 }
       
  1188             }
       
  1189             result = result.resolve(element);
       
  1190         }
       
  1191 
       
  1192         // finally check that file exists
       
  1193         try {
       
  1194             UnixFileAttributes.get(result, true);
       
  1195         } catch (UnixException x) {
       
  1196             x.rethrowAsIOException(result);
       
  1197         }
       
  1198         return result;
       
  1199     }
       
  1200 
       
  1201     @Override
       
  1202     public boolean isHidden() {
       
  1203         checkRead();
       
  1204         UnixPath name = getName();
       
  1205         if (name == null)
       
  1206             return false;
       
  1207         return (name.asByteArray()[0] == '.');
       
  1208     }
       
  1209 
       
  1210     @Override
       
  1211     public URI toUri() {
       
  1212         return UnixUriUtils.toUri(this);
       
  1213     }
       
  1214 
       
  1215     @Override
       
  1216     public WatchKey register(WatchService watcher,
       
  1217                              WatchEvent.Kind<?>[] events,
       
  1218                              WatchEvent.Modifier... modifiers)
       
  1219         throws IOException
       
  1220     {
       
  1221         if (watcher == null)
       
  1222             throw new NullPointerException();
       
  1223         if (!(watcher instanceof AbstractWatchService))
       
  1224             throw new ProviderMismatchException();
       
  1225         checkRead();
       
  1226         return ((AbstractWatchService)watcher).register(this, events, modifiers);
       
  1227     }
       
  1228 }