src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java

Print this page


   1 /*
   2  * Copyright (c) 2009, 2014, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  42  */
  43 
  44 final class ZipPath implements Path {
  45 
  46     private final ZipFileSystem zfs;
  47     private final byte[] path;
  48     private volatile int[] offsets;
  49     private int hashcode = 0;  // cached hashcode (created lazily)
  50 
  51     ZipPath(ZipFileSystem zfs, byte[] path) {
  52         this(zfs, path, false);
  53     }
  54 
  55     ZipPath(ZipFileSystem zfs, byte[] path, boolean normalized) {
  56         this.zfs = zfs;
  57         if (normalized) {
  58             this.path = path;
  59         } else {
  60             if (zfs.zc.isUTF8()) {
  61                 this.path = normalize(path);
  62             } else {
  63                 // see normalize(String);
  64                 this.path = normalize(zfs.getString(path));
  65             }
  66         }
  67     }
  68 
  69     ZipPath(ZipFileSystem zfs, String path) {
  70         this.zfs = zfs;
  71         if (zfs.zc.isUTF8()) {
  72             this.path = normalize(zfs.getBytes(path));
  73         } else {
  74             // see normalize(String);
  75             this.path = normalize(path);
  76         }
  77     }
  78 
  79     @Override
  80     public ZipPath getRoot() {
  81         if (this.isAbsolute())
  82             return zfs.getRootDir();
  83         else
  84             return null;
  85     }
  86 
  87     @Override
  88     public Path getFileName() {
  89         initOffsets();
  90         int count = offsets.length;
  91         if (count == 0)
  92             return null;  // no elements so no name
  93         if (count == 1 && path[0] != '/')
  94             return this;
  95         int lastOffset = offsets[count-1];
  96         int len = path.length - lastOffset;
  97         byte[] result = new byte[len];
  98         System.arraycopy(path, lastOffset, result, 0, len);
  99         return new ZipPath(zfs, result);
 100     }
 101 
 102     @Override
 103     public ZipPath getParent() {
 104         initOffsets();
 105         int count = offsets.length;
 106         if (count == 0)    // no elements so no parent
 107             return null;
 108         int len = offsets[count-1] - 1;
 109         if (len <= 0)      // parent is root only (may be null)
 110             return getRoot();
 111         byte[] result = new byte[len];
 112         System.arraycopy(path, 0, result, 0, len);
 113         return new ZipPath(zfs, result);
 114     }
 115 
 116     @Override
 117     public int getNameCount() {
 118         initOffsets();
 119         return offsets.length;
 120     }
 121 
 122     @Override
 123     public ZipPath getName(int index) {
 124         initOffsets();
 125         if (index < 0 || index >= offsets.length)
 126             throw new IllegalArgumentException();
 127         int begin = offsets[index];
 128         int len;
 129         if (index == (offsets.length-1))
 130             len = path.length - begin;
 131         else
 132             len = offsets[index+1] - begin - 1;
 133         // construct result


 260             result[pos++] = (byte)'.';
 261             result[pos++] = (byte)'.';
 262             if (pos < len)       // no tailing slash at the end
 263                 result[pos++] = (byte)'/';
 264             dotdots--;
 265         }
 266         if (i < oc)
 267             System.arraycopy(o.path, o.offsets[i],
 268                              result, pos,
 269                              o.path.length - o.offsets[i]);
 270         return new ZipPath(zfs, result);
 271     }
 272 
 273     @Override
 274     public ZipFileSystem getFileSystem() {
 275         return zfs;
 276     }
 277 
 278     @Override
 279     public boolean isAbsolute() {
 280         return (this.path.length > 0 && path[0] == '/');
 281     }
 282 
 283     @Override
 284     public ZipPath resolve(Path other) {
 285         final ZipPath o = checkPath(other);
 286         int tlen = this.path.length;
 287         if (tlen == 0 || o.isAbsolute())
 288             return o;
 289         int olen = o.path.length;
 290         if (olen == 0)
 291             return this;







 292         byte[] resolved = null;
 293         if (this.path[tlen - 1] == '/') {



 294             resolved = new byte[tlen + olen];
 295             System.arraycopy(path, 0, resolved, 0, tlen);
 296             System.arraycopy(o.path, 0, resolved, tlen, olen);
 297         } else {
 298             resolved = new byte[tlen + 1 + olen];
 299             System.arraycopy(path, 0, resolved, 0, tlen);
 300             resolved[tlen] = '/';
 301             System.arraycopy(o.path, 0, resolved, tlen + 1, olen);
 302         }
 303         return new ZipPath(zfs, resolved);
 304     }
 305 
 306     @Override
 307     public Path resolveSibling(Path other) {
 308         Objects.requireNonNull(other, "other");
 309         Path parent = getParent();
 310         return (parent == null) ? other : parent.resolve(other);
 311     }
 312 
 313     @Override
 314     public boolean startsWith(Path other) {
 315         final ZipPath o = checkPath(other);
 316         if (o.isAbsolute() != this.isAbsolute() ||
 317             o.path.length > this.path.length)
 318             return false;
 319         int olast = o.path.length;
 320         for (int i = 0; i < olast; i++) {
 321             if (o.path[i] != this.path[i])
 322                 return false;
 323         }


 334         if (olast > 0 && o.path[olast] == '/')
 335             olast--;
 336         int last = this.path.length - 1;
 337         if (last > 0 && this.path[last] == '/')
 338             last--;
 339         if (olast == -1)    // o.path.length == 0
 340             return last == -1;
 341         if ((o.isAbsolute() &&(!this.isAbsolute() || olast != last)) ||
 342             (last < olast))
 343             return false;
 344         for (; olast >= 0; olast--, last--) {
 345             if (o.path[olast] != this.path[last])
 346                 return false;
 347         }
 348         return o.path[olast + 1] == '/' ||
 349                last == -1 || this.path[last] == '/';
 350     }
 351 
 352     @Override
 353     public ZipPath resolve(String other) {
 354         return resolve(zfs.getPath(other));





 355     }
 356 
 357     @Override
 358     public final Path resolveSibling(String other) {
 359         return resolveSibling(zfs.getPath(other));
 360     }
 361 
 362     @Override
 363     public final boolean startsWith(String other) {
 364         return startsWith(zfs.getPath(other));
 365     }
 366 
 367     @Override
 368     public final boolean endsWith(String other) {
 369         return endsWith(zfs.getPath(other));
 370     }
 371 
 372     @Override
 373     public Path normalize() {
 374         byte[] resolved = getResolved();


 438             resolved = r;
 439         }
 440         return resolved;
 441     }
 442 
 443     // removes redundant slashs, replace "\" to zip separator "/"
 444     // and check for invalid characters
 445     private byte[] normalize(byte[] path) {
 446         int len = path.length;
 447         if (len == 0)
 448             return path;
 449         byte prevC = 0;
 450         for (int i = 0; i < len; i++) {
 451             byte c = path[i];
 452             if (c == '\\' || c == '\u0000')
 453                 return normalize(path, i);
 454             if (c == (byte)'/' && prevC == '/')
 455                 return normalize(path, i - 1);
 456             prevC = c;
 457         }
 458         if (len > 1 && prevC == '/')
 459             return Arrays.copyOf(path, len - 1);

 460         return path;
 461     }
 462 
 463     private byte[] normalize(byte[] path, int off) {
 464         byte[] to = new byte[path.length];
 465         int n = 0;
 466         while (n < off) {
 467             to[n] = path[n];
 468             n++;
 469         }
 470         int m = n;
 471         byte prevC = 0;
 472         while (n < path.length) {
 473             byte c = path[n++];
 474             if (c == (byte)'\\')
 475                 c = (byte)'/';
 476             if (c == (byte)'/' && prevC == (byte)'/')
 477                 continue;
 478             if (c == '\u0000')
 479                 throw new InvalidPathException(zfs.getString(path),
 480                                                "Path: nul character not allowed");
 481             to[m++] = c;
 482             prevC = c;
 483         }
 484         if (m > 1 && to[m - 1] == '/')
 485             m--;
 486         return (m == to.length)? to : Arrays.copyOf(to, m);
 487     }
 488 
 489     // if zfs is NOT in utf8, normalize the path as "String"
 490     // to avoid incorrectly normalizing byte '0x5c' (as '\')
 491     // to '/'.
 492     private byte[] normalize(String path) {


 493         int len = path.length();
 494         if (len == 0)
 495             return new byte[0];
 496         char prevC = 0;
 497         for (int i = 0; i < len; i++) {
 498             char c = path.charAt(i);
 499             if (c == '\\' || c == '\u0000')
 500                 return normalize(path, i, len);
 501             if (c == '/' && prevC == '/')
 502                 return normalize(path, i - 1, len);
 503             prevC = c;
 504         }
 505         if (len > 1 && prevC == '/')
 506             path = path.substring(0, len - 1);
 507         return zfs.getBytes(path);
 508     }
 509 
 510     private byte[] normalize(String path, int off, int len) {
 511         StringBuilder to = new StringBuilder(len);
 512         to.append(path, 0, off);


 516             char c = path.charAt(off++);
 517             if (c == '\\')
 518                 c = '/';
 519             if (c == '/' && prevC == '/')
 520                 continue;
 521             if (c == '\u0000')
 522                 throw new InvalidPathException(path,
 523                                                "Path: nul character not allowed");
 524             to.append(c);
 525             prevC = c;
 526         }
 527         len = to.length();
 528         if (len > 1 && prevC == '/')
 529             to.delete(len -1, len);
 530         return zfs.getBytes(to.toString());
 531     }
 532 
 533     // Remove DotSlash(./) and resolve DotDot (..) components
 534     private byte[] getResolved() {
 535         for (int i = 0; i < path.length; i++) {
 536             if (path[i] == (byte)'.') {

 537                 return resolve0();
 538             }
 539         }
 540         return path;
 541     }
 542 
 543     // TBD: performance, avoid initOffsets
 544     private byte[] resolve0() {
 545         byte[] to = new byte[path.length];
 546         int nc = getNameCount();
 547         int[] lastM = new int[nc];
 548         int lastMOff = -1;
 549         int m = 0;
 550         for (int i = 0; i < nc; i++) {
 551             int n = offsets[i];
 552             int len = (i == offsets.length - 1)?
 553                       (path.length - n):(offsets[i + 1] - n - 1);
 554             if (len == 1 && path[n] == (byte)'.') {
 555                 if (m == 0 && path[0] == '/')   // absolute path
 556                     to[m++] = '/';


 959             }
 960             if (c != '%' || betweenBrackets ) {
 961                 sb.append(c);
 962                 i++;
 963                 continue;
 964             }
 965             int nb = 0;
 966             while (c == '%') {
 967                 assert (n - i >= 2);
 968                 bb[nb++] = (byte)(((decode(s.charAt(++i)) & 0xf) << 4) |
 969                                   (decode(s.charAt(++i)) & 0xf));
 970                 if (++i >= n) {
 971                     break;
 972                 }
 973                 c = s.charAt(i);
 974             }
 975             sb.append(new String(bb, 0, nb, UTF_8));
 976         }
 977         return sb.toString();
 978     }
 979 
 980 }
   1 /*
   2  * Copyright (c) 2009, 2017, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  42  */
  43 
  44 final class ZipPath implements Path {
  45 
  46     private final ZipFileSystem zfs;
  47     private final byte[] path;
  48     private volatile int[] offsets;
  49     private int hashcode = 0;  // cached hashcode (created lazily)
  50 
  51     ZipPath(ZipFileSystem zfs, byte[] path) {
  52         this(zfs, path, false);
  53     }
  54 
  55     ZipPath(ZipFileSystem zfs, byte[] path, boolean normalized) {
  56         this.zfs = zfs;
  57         if (normalized) {
  58             this.path = path;
  59         } else {
  60             if (zfs.zc.isUTF8()) {
  61                 this.path = normalize(path);
  62             } else {    // see normalize(String);

  63                 this.path = normalize(zfs.getString(path));
  64             }
  65         }
  66     }
  67 
  68     ZipPath(ZipFileSystem zfs, String path) {
  69         this.zfs = zfs;




  70         this.path = normalize(path);
  71     }

  72 
  73     @Override
  74     public ZipPath getRoot() {
  75         if (this.isAbsolute())
  76             return zfs.getRootDir();
  77         else
  78             return null;
  79     }
  80 
  81    @Override
  82     public Path getFileName() {
  83         int off = path.length;
  84         if (off == 0 || off == 1 && path[0] == '/')
  85             return null;
  86         while (--off >= 0 && path[off] != '/') {}
  87         if (off < 0)
  88             return this;
  89         off++;
  90         byte[] result = new byte[path.length - off];
  91         System.arraycopy(path, off, result, 0, result.length);
  92         return new ZipPath(getFileSystem(), result, true);

  93     }
  94 
  95     @Override
  96     public ZipPath getParent() {
  97         int off = path.length;
  98         if (off == 0 || off == 1 && path[0] == '/')

  99             return null;
 100         while (--off >= 0 && path[off] != '/') {}
 101         if (off <= 0)
 102             return getRoot();
 103         byte[] result = new byte[off];
 104         System.arraycopy(path, 0, result, 0, off);
 105         return new ZipPath(getFileSystem(), result, true);
 106     }
 107 
 108     @Override
 109     public int getNameCount() {
 110         initOffsets();
 111         return offsets.length;
 112     }
 113 
 114     @Override
 115     public ZipPath getName(int index) {
 116         initOffsets();
 117         if (index < 0 || index >= offsets.length)
 118             throw new IllegalArgumentException();
 119         int begin = offsets[index];
 120         int len;
 121         if (index == (offsets.length-1))
 122             len = path.length - begin;
 123         else
 124             len = offsets[index+1] - begin - 1;
 125         // construct result


 252             result[pos++] = (byte)'.';
 253             result[pos++] = (byte)'.';
 254             if (pos < len)       // no tailing slash at the end
 255                 result[pos++] = (byte)'/';
 256             dotdots--;
 257         }
 258         if (i < oc)
 259             System.arraycopy(o.path, o.offsets[i],
 260                              result, pos,
 261                              o.path.length - o.offsets[i]);
 262         return new ZipPath(zfs, result);
 263     }
 264 
 265     @Override
 266     public ZipFileSystem getFileSystem() {
 267         return zfs;
 268     }
 269 
 270     @Override
 271     public boolean isAbsolute() {
 272         return path.length > 0 && path[0] == '/';
 273     }
 274 
 275     @Override
 276     public ZipPath resolve(Path other) {
 277         ZipPath o = checkPath(other);
 278         if (o.path.length == 0)




 279             return this;
 280         if (o.isAbsolute() || this.path.length == 0)
 281             return o;
 282         return resolve(o.path);
 283     }
 284 
 285     // opath is normalized, just concat
 286     private ZipPath resolve(byte[] opath) {
 287         byte[] resolved = null;
 288         byte[] tpath = this.path;
 289         int tlen = tpath.length;
 290         int olen = opath.length;
 291         if (path[tlen - 1] == '/') {
 292             resolved = new byte[tlen + olen];
 293             System.arraycopy(tpath, 0, resolved, 0, tlen);
 294             System.arraycopy(opath, 0, resolved, tlen, olen);
 295         } else {
 296             resolved = new byte[tlen + 1 + olen];
 297             System.arraycopy(tpath, 0, resolved, 0, tlen);
 298             resolved[tlen] = '/';
 299             System.arraycopy(opath, 0, resolved, tlen + 1, olen);
 300         }
 301         return new ZipPath(zfs, resolved, true);
 302     }
 303 
 304     @Override
 305     public Path resolveSibling(Path other) {
 306         Objects.requireNonNull(other, "other");
 307         Path parent = getParent();
 308         return (parent == null) ? other : parent.resolve(other);
 309     }
 310 
 311     @Override
 312     public boolean startsWith(Path other) {
 313         final ZipPath o = checkPath(other);
 314         if (o.isAbsolute() != this.isAbsolute() ||
 315             o.path.length > this.path.length)
 316             return false;
 317         int olast = o.path.length;
 318         for (int i = 0; i < olast; i++) {
 319             if (o.path[i] != this.path[i])
 320                 return false;
 321         }


 332         if (olast > 0 && o.path[olast] == '/')
 333             olast--;
 334         int last = this.path.length - 1;
 335         if (last > 0 && this.path[last] == '/')
 336             last--;
 337         if (olast == -1)    // o.path.length == 0
 338             return last == -1;
 339         if ((o.isAbsolute() &&(!this.isAbsolute() || olast != last)) ||
 340             (last < olast))
 341             return false;
 342         for (; olast >= 0; olast--, last--) {
 343             if (o.path[olast] != this.path[last])
 344                 return false;
 345         }
 346         return o.path[olast + 1] == '/' ||
 347                last == -1 || this.path[last] == '/';
 348     }
 349 
 350     @Override
 351     public ZipPath resolve(String other) {
 352         byte[] opath = normalize(other);
 353         if (opath.length == 0)
 354             return this;
 355         if (opath[0] == '/' || this.path.length == 0)
 356             return new ZipPath(zfs, opath, true);
 357         return resolve(opath);
 358     }
 359 
 360     @Override
 361     public final Path resolveSibling(String other) {
 362         return resolveSibling(zfs.getPath(other));
 363     }
 364 
 365     @Override
 366     public final boolean startsWith(String other) {
 367         return startsWith(zfs.getPath(other));
 368     }
 369 
 370     @Override
 371     public final boolean endsWith(String other) {
 372         return endsWith(zfs.getPath(other));
 373     }
 374 
 375     @Override
 376     public Path normalize() {
 377         byte[] resolved = getResolved();


 441             resolved = r;
 442         }
 443         return resolved;
 444     }
 445 
 446     // removes redundant slashs, replace "\" to zip separator "/"
 447     // and check for invalid characters
 448     private byte[] normalize(byte[] path) {
 449         int len = path.length;
 450         if (len == 0)
 451             return path;
 452         byte prevC = 0;
 453         for (int i = 0; i < len; i++) {
 454             byte c = path[i];
 455             if (c == '\\' || c == '\u0000')
 456                 return normalize(path, i);
 457             if (c == (byte)'/' && prevC == '/')
 458                 return normalize(path, i - 1);
 459             prevC = c;
 460         }
 461         if (len > 1 && prevC == '/') {
 462             return Arrays.copyOf(path, len - 1);
 463         }
 464         return path;
 465     }
 466 
 467     private byte[] normalize(byte[] path, int off) {
 468         byte[] to = new byte[path.length];
 469         int n = 0;
 470         while (n < off) {
 471             to[n] = path[n];
 472             n++;
 473         }
 474         int m = n;
 475         byte prevC = 0;
 476         while (n < path.length) {
 477             byte c = path[n++];
 478             if (c == (byte)'\\')
 479                 c = (byte)'/';
 480             if (c == (byte)'/' && prevC == (byte)'/')
 481                 continue;
 482             if (c == '\u0000')
 483                 throw new InvalidPathException(zfs.getString(path),
 484                                                "Path: nul character not allowed");
 485             to[m++] = c;
 486             prevC = c;
 487         }
 488         if (m > 1 && to[m - 1] == '/')
 489             m--;
 490         return (m == to.length)? to : Arrays.copyOf(to, m);
 491     }
 492 
 493     // if zfs is NOT in utf8, normalize the path as "String"
 494     // to avoid incorrectly normalizing byte '0x5c' (as '\')
 495     // to '/'.
 496     private byte[] normalize(String path) {
 497         if (zfs.zc.isUTF8())
 498             return normalize(zfs.getBytes(path));
 499         int len = path.length();
 500         if (len == 0)
 501             return new byte[0];
 502         char prevC = 0;
 503         for (int i = 0; i < len; i++) {
 504             char c = path.charAt(i);
 505             if (c == '\\' || c == '\u0000')
 506                 return normalize(path, i, len);
 507             if (c == '/' && prevC == '/')
 508                 return normalize(path, i - 1, len);
 509             prevC = c;
 510         }
 511         if (len > 1 && prevC == '/')
 512             path = path.substring(0, len - 1);
 513         return zfs.getBytes(path);
 514     }
 515 
 516     private byte[] normalize(String path, int off, int len) {
 517         StringBuilder to = new StringBuilder(len);
 518         to.append(path, 0, off);


 522             char c = path.charAt(off++);
 523             if (c == '\\')
 524                 c = '/';
 525             if (c == '/' && prevC == '/')
 526                 continue;
 527             if (c == '\u0000')
 528                 throw new InvalidPathException(path,
 529                                                "Path: nul character not allowed");
 530             to.append(c);
 531             prevC = c;
 532         }
 533         len = to.length();
 534         if (len > 1 && prevC == '/')
 535             to.delete(len -1, len);
 536         return zfs.getBytes(to.toString());
 537     }
 538 
 539     // Remove DotSlash(./) and resolve DotDot (..) components
 540     private byte[] getResolved() {
 541         for (int i = 0; i < path.length; i++) {
 542             if (path[i] == (byte)'.' &&
 543                 (i + 1 == path.length || path[i + 1] == '/')) {
 544                 return resolve0();
 545             }
 546         }
 547         return path;
 548     }
 549 
 550     // TBD: performance, avoid initOffsets
 551     private byte[] resolve0() {
 552         byte[] to = new byte[path.length];
 553         int nc = getNameCount();
 554         int[] lastM = new int[nc];
 555         int lastMOff = -1;
 556         int m = 0;
 557         for (int i = 0; i < nc; i++) {
 558             int n = offsets[i];
 559             int len = (i == offsets.length - 1)?
 560                       (path.length - n):(offsets[i + 1] - n - 1);
 561             if (len == 1 && path[n] == (byte)'.') {
 562                 if (m == 0 && path[0] == '/')   // absolute path
 563                     to[m++] = '/';


 966             }
 967             if (c != '%' || betweenBrackets ) {
 968                 sb.append(c);
 969                 i++;
 970                 continue;
 971             }
 972             int nb = 0;
 973             while (c == '%') {
 974                 assert (n - i >= 2);
 975                 bb[nb++] = (byte)(((decode(s.charAt(++i)) & 0xf) << 4) |
 976                                   (decode(s.charAt(++i)) & 0xf));
 977                 if (++i >= n) {
 978                     break;
 979                 }
 980                 c = s.charAt(i);
 981             }
 982             sb.append(new String(bb, 0, nb, UTF_8));
 983         }
 984         return sb.toString();
 985     }

 986 }