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 }
|