1 /*
2  * Copyright (c) 2010, 2018, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.net.URI;
29 import java.net.URLDecoder;
30 import java.nio.ByteBuffer;
31 import java.nio.channels.Channels;
32 import java.nio.channels.FileChannel;
33 import java.nio.channels.SeekableByteChannel;
34 import java.nio.file.DirectoryStream;
35 import java.nio.file.FileAlreadyExistsException;
36 import java.nio.file.FileSystem;
37 import java.nio.file.FileSystemAlreadyExistsException;
38 import java.nio.file.FileSystemException;
39 import java.nio.file.FileSystems;
40 import java.nio.file.FileVisitResult;
41 import java.nio.file.Files;
42 import java.nio.file.OpenOption;
43 import java.nio.file.Path;
44 import java.nio.file.Paths;
45 import java.nio.file.SimpleFileVisitor;
46 import java.nio.file.attribute.BasicFileAttributeView;
47 import java.nio.file.attribute.BasicFileAttributes;
48 import java.nio.file.spi.FileSystemProvider;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Collections;
52 import java.util.Enumeration;
53 import java.util.HashMap;
54 import java.util.HashSet;
55 import java.util.Iterator;
56 import java.util.LinkedList;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Random;
60 import java.util.Set;
61 import java.util.concurrent.TimeUnit;
62 import java.util.zip.CRC32;
63 import java.util.zip.ZipEntry;
64 import java.util.zip.ZipFile;
65 import java.util.zip.ZipOutputStream;
66 
67 import static java.nio.file.StandardOpenOption.*;
68 import static java.nio.file.StandardCopyOption.*;
69 
70 /*
71  * Tests various zipfs operations.
72  *
73  * @test
74  * @bug 6990846 7009092 7009085 7015391 7014948 7005986 7017840 7007596
75  *      7157656 8002390 7012868 7012856 8015728 8038500 8040059 8069211
76  *      8131067 8034802 8210899
77  * @summary Test Zip filesystem provider
78  * @modules jdk.zipfs
79  * @run main ZipFSTester
80  * @run main/othervm/java.security.policy=test.policy ZipFSTester
81  */
82 
83 public class ZipFSTester {
84 
main(String[] args)85     public static void main(String[] args) throws Exception {
86         // create JAR file for test, actual contents don't matter
87         Path jarFile = Utils.createJarFile("tester.jar",
88                 "META-INF/MANIFEST.MF",
89                 "dir1/foo",
90                 "dir2/bar",
91                 "dir1/dir3/fooo");
92 
93         try (FileSystem fs = newZipFileSystem(jarFile, Collections.emptyMap())) {
94             test0(fs);
95             test1(fs);
96             test2(fs);   // more tests
97         }
98         testStreamChannel();
99         testTime(jarFile);
100         test8069211();
101         test8131067();
102     }
103 
104     private static Random rdm = new Random();
105 
test0(FileSystem fs)106     static void test0(FileSystem fs)
107         throws Exception
108     {
109         List<String> list = new LinkedList<>();
110         try (ZipFile zf = new ZipFile(fs.toString())) {
111             Enumeration<? extends ZipEntry> zes = zf.entries();
112             while (zes.hasMoreElements()) {
113                 list.add(zes.nextElement().getName());
114             }
115             for (String pname : list) {
116                 Path path = fs.getPath(pname);
117                 if (!Files.exists(path))
118                     throw new RuntimeException("path existence check failed!");
119                 while ((path = path.getParent()) != null) {
120                     if (!Files.exists(path))
121                         throw new RuntimeException("parent existence check failed!");
122                 }
123             }
124         }
125     }
126 
test1(FileSystem fs0)127     static void test1(FileSystem fs0)
128         throws Exception
129     {
130         // prepare a src for testing
131         Path src = getTempPath();
132         String tmpName = src.toString();
133         try (OutputStream os = Files.newOutputStream(src)) {
134             byte[] bits = new byte[12345];
135             rdm.nextBytes(bits);
136             os.write(bits);
137         }
138 
139         // clone a fs from fs0 and test on it
140         Path tmpfsPath = getTempPath();
141         Map<String, Object> env = new HashMap<String, Object>();
142         env.put("create", "true");
143         try (FileSystem copy = newZipFileSystem(tmpfsPath, env)) {
144             z2zcopy(fs0, copy, "/", 0);
145 
146             // copy the test jar itself in
147             Files.copy(Paths.get(fs0.toString()), copy.getPath("/foo.jar"));
148             Path zpath = copy.getPath("/foo.jar");
149             try (FileSystem zzfs = FileSystems.newFileSystem(zpath, null)) {
150                 Files.copy(src, zzfs.getPath("/srcInjarjar"));
151             }
152         }
153 
154         try (FileSystem fs = newZipFileSystem(tmpfsPath, new HashMap<String, Object>())) {
155 
156             FileSystemProvider provider = fs.provider();
157             // newFileSystem(path...) should not throw exception
158             try (FileSystem fsPath = provider.newFileSystem(tmpfsPath, new HashMap<String, Object>())){}
159             try (FileSystem fsUri = provider.newFileSystem(
160                      new URI("jar", tmpfsPath.toUri().toString(), null),
161                      new HashMap<String, Object>()))
162             {
163                 throw new RuntimeException("newFileSystem(URI...) does not throw exception");
164             } catch (FileSystemAlreadyExistsException fsaee) {}
165 
166             try {
167                 provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(),
168                                        new HashMap<String, Object>());
169                 throw new RuntimeException("newFileSystem() opens a directory as zipfs");
170             } catch (UnsupportedOperationException uoe) {}
171 
172             try {
173                 provider.newFileSystem(src, new HashMap<String, Object>());
174                 throw new RuntimeException("newFileSystem() opens a non-zip file as zipfs");
175             } catch (UnsupportedOperationException uoe) {}
176 
177             // walk
178             walk(fs.getPath("/"));
179 
180             // copyin
181             Path dst = getPathWithParents(fs, tmpName);
182             Files.copy(src, dst);
183             checkEqual(src, dst);
184 
185             // copy
186             Path dst2 = getPathWithParents(fs, "/xyz" + rdm.nextInt(100) +
187                                            "/efg" + rdm.nextInt(100) + "/foo.class");
188             Files.copy(dst, dst2);
189             //dst.moveTo(dst2);
190             checkEqual(src, dst2);
191 
192             // delete
193             Files.delete(dst);
194             if (Files.exists(dst))
195                 throw new RuntimeException("Failed!");
196 
197             // moveout
198             Path dst3 = Paths.get(tmpName + "_Tmp");
199             Files.move(dst2, dst3);
200             checkEqual(src, dst3);
201             if (Files.exists(dst2))
202                 throw new RuntimeException("Failed!");
203 
204             // copyback + move
205             Files.copy(dst3, dst);
206             Path dst4 = getPathWithParents(fs, tmpName + "_Tmp0");
207             Files.move(dst, dst4);
208             checkEqual(src, dst4);
209 
210             // delete
211             Files.delete(dst4);
212             if (Files.exists(dst4))
213                 throw new RuntimeException("Failed!");
214             Files.delete(dst3);
215             if (Files.exists(dst3))
216                 throw new RuntimeException("Failed!");
217 
218             // move (existing entry)
219             Path dst5 = fs.getPath("META-INF/MANIFEST.MF");
220             if (Files.exists(dst5)) {
221                 Path dst6 = fs.getPath("META-INF/MANIFEST.MF_TMP");
222                 Files.move(dst5, dst6);
223                 walk(fs.getPath("/"));
224             }
225 
226             // newInputStream on dir
227             Path parent = dst2.getParent();
228             try {
229                 Files.newInputStream(parent);
230                 throw new RuntimeException("Failed");
231             } catch (FileSystemException e) {
232                 // expected fse
233             }
234 
235             // rmdirs
236             try {
237                 rmdirs(parent);
238             } catch (IOException x) {
239                 x.printStackTrace();
240             }
241 
242             // newFileChannel() copy in, out and verify via fch
243             fchCopy(src, dst);    // in
244             checkEqual(src, dst);
245             Path tmp = Paths.get(tmpName + "_Tmp");
246             fchCopy(dst, tmp);   //  out
247             checkEqual(src, tmp);
248             Files.delete(tmp);
249 
250             // test channels
251             channel(fs, dst);
252             Files.delete(dst);
253 
254             // test foo.jar in jar/zipfs #8034802
255             Path jpath = fs.getPath("/foo.jar");
256             System.out.println("walking: " + jpath);
257             try (FileSystem zzfs = FileSystems.newFileSystem(jpath, null)) {
258                 walk(zzfs.getPath("/"));
259                 // foojar:/srcInjarjar
260                 checkEqual(src, zzfs.getPath("/srcInjarjar"));
261 
262                 dst = getPathWithParents(zzfs, tmpName);
263                 fchCopy(src, dst);
264                 checkEqual(src, dst);
265                 tmp = Paths.get(tmpName + "_Tmp");
266                 fchCopy(dst, tmp);   //  out
267                 checkEqual(src, tmp);
268                 Files.delete(tmp);
269 
270                 channel(zzfs, dst);
271                 Files.delete(dst);
272             }
273         } finally {
274             Files.deleteIfExists(tmpfsPath);
275             Files.deleteIfExists(src);
276         }
277     }
278 
test2(FileSystem fs)279     static void test2(FileSystem fs) throws Exception {
280 
281         Path fs1Path = getTempPath();
282         Path fs2Path = getTempPath();
283         Path fs3Path = getTempPath();
284 
285         // create a new filesystem, copy everything from fs
286         Map<String, Object> env = new HashMap<String, Object>();
287         env.put("create", "true");
288         FileSystem fs0 = newZipFileSystem(fs1Path, env);
289 
290         final FileSystem fs2 = newZipFileSystem(fs2Path, env);
291         final FileSystem fs3 = newZipFileSystem(fs3Path, env);
292 
293         System.out.println("copy src: fs -> fs0...");
294         z2zcopy(fs, fs0, "/", 0);   // copy fs -> fs1
295         fs0.close();                // dump to file
296 
297         System.out.println("open fs0 as fs1");
298         env = new HashMap<String, Object>();
299         final FileSystem fs1 = newZipFileSystem(fs1Path, env);
300 
301         System.out.println("listing...");
302         final ArrayList<String> files = new ArrayList<>();
303         final ArrayList<String> dirs = new ArrayList<>();
304         list(fs1.getPath("/"), files, dirs);
305 
306         Thread t0 = new Thread(new Runnable() {
307             public void run() {
308                 List<String> list = new ArrayList<>(dirs);
309                 Collections.shuffle(list);
310                 for (String path : list) {
311                     try {
312                         z2zcopy(fs1, fs2, path, 0);
313                     } catch (Exception x) {
314                         x.printStackTrace();
315                     }
316                 }
317             }
318 
319         });
320 
321         Thread t1 = new Thread(new Runnable() {
322             public void run() {
323                 List<String> list = new ArrayList<>(dirs);
324                 Collections.shuffle(list);
325                 for (String path : list) {
326                     try {
327                         z2zcopy(fs1, fs2, path, 1);
328                     } catch (Exception x) {
329                         x.printStackTrace();
330                     }
331                 }
332             }
333 
334         });
335 
336         Thread t2 = new Thread(new Runnable() {
337             public void run() {
338                 List<String> list = new ArrayList<>(dirs);
339                 Collections.shuffle(list);
340                 for (String path : list) {
341                     try {
342                         z2zcopy(fs1, fs2, path, 2);
343                     } catch (Exception x) {
344                         x.printStackTrace();
345                     }
346                 }
347             }
348 
349         });
350 
351         Thread t3 = new Thread(new Runnable() {
352             public void run() {
353                 List<String> list = new ArrayList<>(files);
354                 Collections.shuffle(list);
355                 while (!list.isEmpty()) {
356                     Iterator<String> itr = list.iterator();
357                     while (itr.hasNext()) {
358                         String path = itr.next();
359                         try {
360                             if (Files.exists(fs2.getPath(path))) {
361                                 z2zmove(fs2, fs3, path);
362                                 itr.remove();
363                             }
364                         } catch (FileAlreadyExistsException x){
365                             itr.remove();
366                         } catch (Exception x) {
367                             x.printStackTrace();
368                         }
369                     }
370                 }
371             }
372 
373         });
374 
375         System.out.println("copying/removing...");
376         t0.start(); t1.start(); t2.start(); t3.start();
377         t0.join(); t1.join(); t2.join(); t3.join();
378 
379         System.out.println("closing: fs1, fs2");
380         fs1.close();
381         fs2.close();
382 
383         int failed = 0;
384         System.out.println("checkEqual: fs vs fs3");
385         for (String path : files) {
386             try {
387                 checkEqual(fs.getPath(path), fs3.getPath(path));
388             } catch (IOException x) {
389                 //x.printStackTrace();
390                 failed++;
391             }
392         }
393         System.out.println("closing: fs3");
394         fs3.close();
395 
396         System.out.println("opening: fs3 as fs4");
397         FileSystem fs4 = newZipFileSystem(fs3Path, env);
398 
399 
400         ArrayList<String> files2 = new ArrayList<>();
401         ArrayList<String> dirs2 = new ArrayList<>();
402         list(fs4.getPath("/"), files2, dirs2);
403 
404         System.out.println("checkEqual: fs vs fs4");
405         for (String path : files2) {
406             checkEqual(fs.getPath(path), fs4.getPath(path));
407         }
408         System.out.println("walking: fs4");
409         walk(fs4.getPath("/"));
410         System.out.println("closing: fs4");
411         fs4.close();
412         System.out.printf("failed=%d%n", failed);
413 
414         Files.delete(fs1Path);
415         Files.delete(fs2Path);
416         Files.delete(fs3Path);
417     }
418 
419     static final int METHOD_STORED     = 0;
420     static final int METHOD_DEFLATED   = 8;
421 
getEntries()422     static Object[][] getEntries() {
423         Object[][] entries = new Object[10 + rdm.nextInt(20)][3];
424         // first entries shall test the corner case of 0 bytes of data
425         entries[0][0] = "entries" + 0;
426         entries[0][1] = METHOD_STORED;
427         entries[0][2] = new byte[0];
428         entries[1][0] = "entries" + 1;
429         entries[1][1] = METHOD_DEFLATED;
430         entries[1][2] = new byte[0];
431         // the rest is random data
432         for (int i = 2; i < entries.length; i++) {
433             entries[i][0] = "entries" + i;
434             entries[i][1] = rdm.nextInt(10) % 2 == 0 ?
435                 METHOD_STORED : METHOD_DEFLATED;
436             entries[i][2] = new byte[rdm.nextInt(8192)];
437             rdm.nextBytes((byte[])entries[i][2]);
438         }
439         return entries;
440     }
441 
442     // check the content of read from zipfs is equal to the "bytes"
checkRead(Path path, byte[] expected)443     private static void checkRead(Path path, byte[] expected) throws IOException {
444 
445         // fileAttribute
446         CRC32 crc32 = new CRC32();
447         crc32.update(expected);
448 
449         if (((Long)Files.getAttribute(path, "zip:crc")).intValue() !=
450             (int)crc32.getValue()) {
451             System.out.printf(" getAttribute.crc <%s> failed %x vs %x ...%n",
452                               path.toString(),
453                               ((Long)Files.getAttribute(path, "zip:crc")).intValue(),
454                               (int)crc32.getValue());
455             throw new RuntimeException("CHECK FAILED!");
456         }
457 
458         if (((Long)Files.getAttribute(path, "zip:size")).intValue() != expected.length) {
459             System.out.printf(" getAttribute.size <%s> failed %x vs %x ...%n",
460                               path.toString(),
461                               ((Long)Files.getAttribute(path, "zip:size")).intValue(),
462                               expected.length);
463             throw new RuntimeException("CHECK FAILED!");
464         }
465 
466         //streams
467         try (InputStream is = Files.newInputStream(path)) {
468             if (!Arrays.equals(is.readAllBytes(), expected)) {
469                 System.out.printf(" newInputStream <%s> failed...%n", path.toString());
470                 throw new RuntimeException("CHECK FAILED!");
471             }
472         }
473 
474         // channels -- via sun.nio.ch.ChannelInputStream
475         try (SeekableByteChannel sbc = Files.newByteChannel(path);
476             InputStream is = Channels.newInputStream(sbc)) {
477 
478             // check all bytes match
479             if (!Arrays.equals(is.readAllBytes(), expected)) {
480                 System.out.printf(" newByteChannel <%s> failed...%n", path.toString());
481                 throw new RuntimeException("CHECK FAILED!");
482             }
483 
484             // Check if read position is at the end
485             if (sbc.position() != expected.length) {
486                 System.out.printf("pos [%s]: size=%d, position=%d%n",
487                                   path.toString(), expected.length, sbc.position());
488                 throw new RuntimeException("CHECK FAILED!");
489             }
490 
491             // Check position(x) + read() at the random/specific pos/len
492             byte[] buf = new byte[1024];
493             ByteBuffer bb = ByteBuffer.wrap(buf);
494             for (int i = 0; i < 10; i++) {
495                 int pos = 0;
496                 int len = 0;
497                 if (expected.length > 0) {
498                     pos = rdm.nextInt((int) sbc.size());
499                     len = rdm.nextInt(Math.min(buf.length, expected.length - pos));
500                 }
501                 // System.out.printf("  --> %d, %d%n", pos, len);
502                 bb.position(0).limit(len);    // bb.flip().limit(len);
503                 if (sbc.position(pos).position() != pos ||
504                     sbc.read(bb) != len ||
505                     !Arrays.equals(buf, 0, bb.position(), expected, pos, pos + len)) {
506                     System.out.printf("read()/position() failed%n");
507                 }
508             }
509         } catch (IOException x) {
510             x.printStackTrace();
511             throw new RuntimeException("CHECK FAILED!");
512         }
513     }
514 
515     // test entry stream/channel reading
testStreamChannel()516     static void testStreamChannel() throws Exception {
517         Path zpath = getTempPath();
518         try {
519             var crc = new CRC32();
520             Object[][] entries = getEntries();
521 
522             // [1] create zip via ZipOutputStream
523             try (var os = Files.newOutputStream(zpath);
524                  var zos = new ZipOutputStream(os)) {
525                 for (Object[] entry : entries) {
526                    var ze = new ZipEntry((String)entry[0]);
527                    int method = (int)entry[1];
528                    byte[] bytes = (byte[])entry[2];
529                    if (method == METHOD_STORED) {
530                        ze.setSize(bytes.length);
531                        crc.reset();
532                        crc.update(bytes);
533                        ze.setCrc(crc.getValue());
534                    }
535                    ze.setMethod(method);
536                    zos.putNextEntry(ze);
537                    zos.write(bytes);
538                    zos.closeEntry();
539                 }
540             }
541             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
542                 for (Object[] e : entries) {
543                     Path path = zfs.getPath((String)e[0]);
544                     int method = (int)e[1];
545                     byte[] bytes = (byte[])e[2];
546                     // System.out.printf("checking read [%s, %d, %d]%n",
547                     //                   path.toString(), bytes.length, method);
548                     checkRead(path, bytes);
549                 }
550             }
551             Files.deleteIfExists(zpath);
552 
553             // [2] create zip via zfs.newByteChannel
554             try (var zfs = newZipFileSystem(zpath, Map.of("create", "true"))) {
555                 for (Object[] e : entries) {
556                     //  tbd: method is not used
557                     try (var sbc = Files.newByteChannel(zfs.getPath((String)e[0]),
558                                                         CREATE_NEW, WRITE)) {
559                         sbc.write(ByteBuffer.wrap((byte[])e[2]));
560                     }
561                 }
562             }
563             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
564                 for (Object[] e : entries) {
565                     checkRead(zfs.getPath((String)e[0]), (byte[])e[2]);
566                 }
567             }
568             Files.deleteIfExists(zpath);
569 
570             // [3] create zip via Files.write()/newoutputStream/
571             try (var zfs = newZipFileSystem(zpath, Map.of("create", "true"))) {
572                 for (Object[] e : entries) {
573                     Files.write(zfs.getPath((String)e[0]), (byte[])e[2]);
574                 }
575             }
576             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
577                 for (Object[] e : entries) {
578                     checkRead(zfs.getPath((String)e[0]), (byte[])e[2]);
579                 }
580             }
581             Files.deleteIfExists(zpath);
582 
583             // [4] create zip via zfs.newByteChannel, with "method_stored"
584             try (var zfs = newZipFileSystem(zpath,
585                     Map.of("create", true, "noCompression", true))) {
586                 for (Object[] e : entries) {
587                     try (var sbc = Files.newByteChannel(zfs.getPath((String)e[0]),
588                                                         CREATE_NEW, WRITE)) {
589                         sbc.write(ByteBuffer.wrap((byte[])e[2]));
590                     }
591                 }
592             }
593             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
594                 for (Object[] e : entries) {
595                     checkRead(zfs.getPath((String)e[0]), (byte[])e[2]);
596                 }
597             }
598             Files.deleteIfExists(zpath);
599 
600         } finally {
601             Files.deleteIfExists(zpath);
602         }
603     }
604 
605     // test file stamp
testTime(Path src)606     static void testTime(Path src) throws Exception {
607         BasicFileAttributes attrs = Files
608                         .getFileAttributeView(src, BasicFileAttributeView.class)
609                         .readAttributes();
610         // create a new filesystem, copy this file into it
611         Map<String, Object> env = new HashMap<String, Object>();
612         env.put("create", "true");
613         Path fsPath = getTempPath();
614         try (FileSystem fs = newZipFileSystem(fsPath, env)) {
615             System.out.println("test copy with timestamps...");
616             // copyin
617             Path dst = getPathWithParents(fs, "me");
618             Files.copy(src, dst, COPY_ATTRIBUTES);
619             checkEqual(src, dst);
620             System.out.println("mtime: " + attrs.lastModifiedTime());
621             System.out.println("ctime: " + attrs.creationTime());
622             System.out.println("atime: " + attrs.lastAccessTime());
623             System.out.println(" ==============>");
624             BasicFileAttributes dstAttrs = Files
625                             .getFileAttributeView(dst, BasicFileAttributeView.class)
626                             .readAttributes();
627             System.out.println("mtime: " + dstAttrs.lastModifiedTime());
628             System.out.println("ctime: " + dstAttrs.creationTime());
629             System.out.println("atime: " + dstAttrs.lastAccessTime());
630 
631             // 1-second granularity
632             if (attrs.lastModifiedTime().to(TimeUnit.SECONDS) !=
633                 dstAttrs.lastModifiedTime().to(TimeUnit.SECONDS) ||
634                 attrs.lastAccessTime().to(TimeUnit.SECONDS) !=
635                 dstAttrs.lastAccessTime().to(TimeUnit.SECONDS) ||
636                 attrs.creationTime().to(TimeUnit.SECONDS) !=
637                 dstAttrs.creationTime().to(TimeUnit.SECONDS)) {
638                 throw new RuntimeException("Timestamp Copy Failed!");
639             }
640         } finally {
641             Files.delete(fsPath);
642         }
643     }
644 
test8069211()645     static void test8069211() throws Exception {
646         // create a new filesystem, copy this file into it
647         Map<String, Object> env = new HashMap<String, Object>();
648         env.put("create", "true");
649         Path fsPath = getTempPath();
650         try (FileSystem fs = newZipFileSystem(fsPath, env);) {
651             OutputStream out = Files.newOutputStream(fs.getPath("/foo"));
652             out.write("hello".getBytes());
653             out.close();
654             out.close();
655         }
656         try (FileSystem fs = newZipFileSystem(fsPath, new HashMap<String, Object>())) {
657             if (!Arrays.equals(Files.readAllBytes(fs.getPath("/foo")),
658                                "hello".getBytes())) {
659                 throw new RuntimeException("entry close() failed");
660             }
661         } catch (Exception x) {
662             throw new RuntimeException("entry close() failed", x);
663         } finally {
664             Files.delete(fsPath);
665         }
666     }
667 
test8131067()668     static void test8131067() throws Exception {
669         Map<String, Object> env = new HashMap<String, Object>();
670         env.put("create", "true");
671 
672         // file name with space character for URI to quote it
673         File tmp = File.createTempFile("test zipfs", "zip");
674         tmp.delete();    // we need a clean path, no file
675         Path fsPath = tmp.toPath();
676         try (FileSystem fs = newZipFileSystem(fsPath, env);) {
677             Files.write(fs.getPath("/foo"), "hello".getBytes());
678             URI fooUri = fs.getPath("/foo").toUri();
679             if (!Arrays.equals(Files.readAllBytes(Paths.get(fooUri)),
680                                "hello".getBytes())) {
681                 throw new RuntimeException("entry close() failed");
682             }
683         } finally {
684             Files.delete(fsPath);
685         }
686     }
687 
newZipFileSystem(Path path, Map<String, ?> env)688     private static FileSystem newZipFileSystem(Path path, Map<String, ?> env)
689         throws Exception
690     {
691         // Use URLDecoder (for test only) to remove the double escaped space
692         // character
693         return FileSystems.newFileSystem(
694             new URI("jar", URLDecoder.decode(path.toUri().toString(), "utf8"),
695                 null), env, null);
696     }
697 
getTempPath()698     private static Path getTempPath() throws IOException
699     {
700         File tmp = File.createTempFile("testzipfs_", "zip");
701         tmp.delete();    // we need a clean path, no file
702         return tmp.toPath();
703     }
704 
list(Path path, List<String> files, List<String> dirs )705     private static void list(Path path, List<String> files, List<String> dirs )
706         throws IOException
707     {
708         if (Files.isDirectory(path)) {
709             try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
710                 for (Path child : ds)
711                     list(child, files, dirs);
712             }
713             dirs.add(path.toString());
714         } else {
715             files.add(path.toString());
716         }
717     }
718 
z2zcopy(FileSystem src, FileSystem dst, String path, int method)719     private static void z2zcopy(FileSystem src, FileSystem dst, String path,
720                                 int method)
721         throws IOException
722     {
723         Path srcPath = src.getPath(path);
724         Path dstPath = dst.getPath(path);
725 
726         if (Files.isDirectory(srcPath)) {
727             if (!Files.exists(dstPath)) {
728                 try {
729                     mkdirs(dstPath);
730                 } catch (FileAlreadyExistsException x) {}
731             }
732             try (DirectoryStream<Path> ds = Files.newDirectoryStream(srcPath)) {
733                 for (Path child : ds) {
734                     z2zcopy(src, dst,
735                            path + (path.endsWith("/")?"":"/") + child.getFileName(),
736                            method);
737                 }
738             }
739         } else {
740             try {
741                 if (Files.exists(dstPath))
742                     return;
743                 switch (method) {
744                 case 0:
745                     Files.copy(srcPath, dstPath);
746                     break;
747                 case 1:
748                     chCopy(srcPath, dstPath);
749                     break;
750                 case 2:
751                     //fchCopy(srcPath, dstPath);
752                     streamCopy(srcPath, dstPath);
753                     break;
754                 }
755             } catch (FileAlreadyExistsException x) {}
756         }
757     }
758 
z2zmove(FileSystem src, FileSystem dst, String path)759     private static void z2zmove(FileSystem src, FileSystem dst, String path)
760         throws IOException
761     {
762         Path srcPath = src.getPath(path);
763         Path dstPath = dst.getPath(path);
764 
765         if (Files.isDirectory(srcPath)) {
766             if (!Files.exists(dstPath))
767                 mkdirs(dstPath);
768             try (DirectoryStream<Path> ds = Files.newDirectoryStream(srcPath)) {
769                 for (Path child : ds) {
770                     z2zmove(src, dst,
771                             path + (path.endsWith("/")?"":"/") + child.getFileName());
772                 }
773             }
774         } else {
775             //System.out.println("moving..." + path);
776             Path parent = dstPath.getParent();
777             if (parent != null && Files.notExists(parent))
778                 mkdirs(parent);
779             Files.move(srcPath, dstPath);
780         }
781     }
782 
walk(Path path)783     private static void walk(Path path) throws IOException
784     {
785         Files.walkFileTree(
786             path,
787             new SimpleFileVisitor<Path>() {
788                 private int indent = 0;
789                 private void indent() {
790                     int n = 0;
791                     while (n++ < indent)
792                         System.out.printf(" ");
793                 }
794 
795                 @Override
796                 public FileVisitResult visitFile(Path file,
797                                                  BasicFileAttributes attrs)
798                 {
799                     indent();
800                     System.out.printf("%s%n", file.getFileName().toString());
801                     return FileVisitResult.CONTINUE;
802                 }
803 
804                 @Override
805                 public FileVisitResult preVisitDirectory(Path dir,
806                                                          BasicFileAttributes attrs)
807                 {
808                     indent();
809                     System.out.printf("[%s]%n", dir.toString());
810                     indent += 2;
811                     return FileVisitResult.CONTINUE;
812                 }
813 
814                 @Override
815                 public FileVisitResult postVisitDirectory(Path dir,
816                                                           IOException ioe)
817                     throws IOException
818                 {
819                     indent -= 2;
820                     return FileVisitResult.CONTINUE;
821                 }
822         });
823     }
824 
mkdirs(Path path)825     private static void mkdirs(Path path) throws IOException {
826         if (Files.exists(path))
827             return;
828         path = path.toAbsolutePath();
829         Path parent = path.getParent();
830         if (parent != null) {
831             if (Files.notExists(parent))
832                 mkdirs(parent);
833         }
834         Files.createDirectory(path);
835     }
836 
rmdirs(Path path)837     private static void rmdirs(Path path) throws IOException {
838         while (path != null && path.getNameCount() != 0) {
839             Files.delete(path);
840             path = path.getParent();
841         }
842     }
843 
844     // check the content of two paths are equal
checkEqual(Path src, Path dst)845     private static void checkEqual(Path src, Path dst) throws IOException
846     {
847         System.out.printf("checking <%s> vs <%s>...%n",
848                           src.toString(), dst.toString());
849 
850         //streams
851         byte[] bufSrc = new byte[8192];
852         byte[] bufDst = new byte[8192];
853         try (InputStream isSrc = Files.newInputStream(src);
854              InputStream isDst = Files.newInputStream(dst))
855         {
856             int nSrc = 0;
857             while ((nSrc = isSrc.read(bufSrc)) != -1) {
858                 int nDst = 0;
859                 while (nDst < nSrc) {
860                     int n = isDst.read(bufDst, nDst, nSrc - nDst);
861                     if (n == -1) {
862                         System.out.printf("checking <%s> vs <%s>...%n",
863                                           src.toString(), dst.toString());
864                         throw new RuntimeException("CHECK FAILED!");
865                     }
866                     nDst += n;
867                 }
868                 while (--nSrc >= 0) {
869                     if (bufSrc[nSrc] != bufDst[nSrc]) {
870                         System.out.printf("checking <%s> vs <%s>...%n",
871                                           src.toString(), dst.toString());
872                         throw new RuntimeException("CHECK FAILED!");
873                     }
874                     nSrc--;
875                 }
876             }
877         }
878 
879         // channels
880         try (SeekableByteChannel chSrc = Files.newByteChannel(src);
881              SeekableByteChannel chDst = Files.newByteChannel(dst))
882         {
883             if (chSrc.size() != chDst.size()) {
884                 System.out.printf("src[%s].size=%d, dst[%s].size=%d%n",
885                                   chSrc.toString(), chSrc.size(),
886                                   chDst.toString(), chDst.size());
887                 throw new RuntimeException("CHECK FAILED!");
888             }
889             ByteBuffer bbSrc = ByteBuffer.allocate(8192);
890             ByteBuffer bbDst = ByteBuffer.allocate(8192);
891 
892             int nSrc = 0;
893             while ((nSrc = chSrc.read(bbSrc)) != -1) {
894                 int nDst = chDst.read(bbDst);
895                 if (nSrc != nDst) {
896                     System.out.printf("checking <%s> vs <%s>...%n",
897                                       src.toString(), dst.toString());
898                     throw new RuntimeException("CHECK FAILED!");
899                 }
900                 while (--nSrc >= 0) {
901                     if (bbSrc.get(nSrc) != bbDst.get(nSrc)) {
902                         System.out.printf("checking <%s> vs <%s>...%n",
903                                           src.toString(), dst.toString());
904                         throw new RuntimeException("CHECK FAILED!");
905                     }
906                     nSrc--;
907                 }
908                 bbSrc.flip();
909                 bbDst.flip();
910             }
911 
912             // Check if source read position is at the end
913             if (chSrc.position() != chSrc.size()) {
914                 System.out.printf("src[%s]: size=%d, position=%d%n",
915                                   chSrc.toString(), chSrc.size(), chSrc.position());
916                 throw new RuntimeException("CHECK FAILED!");
917             }
918 
919             // Check if destination read position is at the end
920             if (chDst.position() != chDst.size()) {
921                 System.out.printf("dst[%s]: size=%d, position=%d%n",
922                                   chDst.toString(), chDst.size(), chDst.position());
923                 throw new RuntimeException("CHECK FAILED!");
924             }
925 
926             // Check position(x) + read() at the specific pos/len
927             for (int i = 0; i < 10; i++) {
928                 int pos = rdm.nextInt((int)chSrc.size());
929                 int limit = rdm.nextInt(1024);
930                 if (chSrc.position(pos).position() != chDst.position(pos).position()) {
931                     System.out.printf("dst/src.position(pos failed%n");
932                 }
933                 bbSrc.clear().limit(limit);
934                 bbDst.clear().limit(limit);
935                 if (chSrc.read(bbSrc) != chDst.read(bbDst) ||
936                     !bbSrc.flip().equals(bbDst.flip())) {
937                     System.out.printf("dst/src.read() failed%n");
938                 }
939             }
940         } catch (IOException x) {
941             x.printStackTrace();
942         }
943     }
944 
fchCopy(Path src, Path dst)945     private static void fchCopy(Path src, Path dst) throws IOException
946     {
947         Set<OpenOption> read = new HashSet<>();
948         read.add(READ);
949         Set<OpenOption> openwrite = new HashSet<>();
950         openwrite.add(CREATE_NEW);
951         openwrite.add(WRITE);
952 
953         try (FileChannel srcFc = src.getFileSystem()
954                                     .provider()
955                                     .newFileChannel(src, read);
956              FileChannel dstFc = dst.getFileSystem()
957                                     .provider()
958                                     .newFileChannel(dst, openwrite))
959         {
960             ByteBuffer bb = ByteBuffer.allocate(8192);
961             while (srcFc.read(bb) >= 0) {
962                 bb.flip();
963                 dstFc.write(bb);
964                 bb.clear();
965             }
966         }
967     }
968 
chCopy(Path src, Path dst)969     private static void chCopy(Path src, Path dst) throws IOException
970     {
971         Set<OpenOption> read = new HashSet<>();
972         read.add(READ);
973         Set<OpenOption> openwrite = new HashSet<>();
974         openwrite.add(CREATE_NEW);
975         openwrite.add(WRITE);
976 
977         try (SeekableByteChannel srcCh = Files.newByteChannel(src, read);
978              SeekableByteChannel dstCh = Files.newByteChannel(dst, openwrite))
979         {
980 
981             ByteBuffer bb = ByteBuffer.allocate(8192);
982             while (srcCh.read(bb) >= 0) {
983                 bb.flip();
984                 dstCh.write(bb);
985                 bb.clear();
986             }
987 
988             // Check if source read position is at the end
989             if (srcCh.position() != srcCh.size()) {
990                 System.out.printf("src[%s]: size=%d, position=%d%n",
991                                   srcCh.toString(), srcCh.size(), srcCh.position());
992                 throw new RuntimeException("CHECK FAILED!");
993             }
994 
995             // Check if destination write position is at the end
996             if (dstCh.position() != dstCh.size()) {
997                 System.out.printf("dst[%s]: size=%d, position=%d%n",
998                                   dstCh.toString(), dstCh.size(), dstCh.position());
999                 throw new RuntimeException("CHECK FAILED!");
1000             }
1001         }
1002     }
1003 
streamCopy(Path src, Path dst)1004     private static void streamCopy(Path src, Path dst) throws IOException
1005     {
1006         byte[] buf = new byte[8192];
1007         try (InputStream isSrc = Files.newInputStream(src);
1008              OutputStream osDst = Files.newOutputStream(dst))
1009         {
1010             int n = 0;
1011             while ((n = isSrc.read(buf)) != -1) {
1012                 osDst.write(buf, 0, n);
1013             }
1014         }
1015     }
1016 
channel(FileSystem fs, Path path)1017     static void channel(FileSystem fs, Path path)
1018         throws Exception
1019     {
1020         System.out.println("test ByteChannel...");
1021         Set<OpenOption> read = new HashSet<>();
1022         read.add(READ);
1023         int n = 0;
1024         ByteBuffer bb = null;
1025         ByteBuffer bb2 = null;
1026         int N = 120;
1027 
1028         try (SeekableByteChannel sbc = Files.newByteChannel(path)) {
1029             System.out.printf("   sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size());
1030             if (sbc.position() != 0) {
1031                 throw new RuntimeException("CHECK FAILED!");
1032             }
1033 
1034             bb = ByteBuffer.allocate((int)sbc.size());
1035             n = sbc.read(bb);
1036             System.out.printf("   sbc[1]: read=%d, pos=%d, size=%d%n",
1037                               n, sbc.position(), sbc.size());
1038             if (sbc.position() != sbc.size()) {
1039                 throw new RuntimeException("CHECK FAILED!");
1040             }
1041             bb2 = ByteBuffer.allocate((int)sbc.size());
1042         }
1043 
1044         // sbc.position(pos) is not supported in current version
1045         // try the FileChannel
1046         try (SeekableByteChannel sbc = fs.provider().newFileChannel(path, read)) {
1047             sbc.position(N);
1048             System.out.printf("   sbc[2]: pos=%d, size=%d%n",
1049                               sbc.position(), sbc.size());
1050             if (sbc.position() != N) {
1051                 throw new RuntimeException("CHECK FAILED!");
1052             }
1053             bb2.limit(100);
1054             n = sbc.read(bb2);
1055             System.out.printf("   sbc[3]: read=%d, pos=%d, size=%d%n",
1056                               n, sbc.position(), sbc.size());
1057             if (n < 0 || sbc.position() != (N + n)) {
1058                 throw new RuntimeException("CHECK FAILED!");
1059             }
1060             System.out.printf("   sbc[4]: bb[%d]=%d, bb1[0]=%d%n",
1061                               N, bb.get(N) & 0xff, bb2.get(0) & 0xff);
1062         }
1063     }
1064 
1065     // create parents if does not exist
getPathWithParents(FileSystem fs, String name)1066     static Path getPathWithParents(FileSystem fs, String name)
1067         throws Exception
1068     {
1069         Path path = fs.getPath(name);
1070         Path parent = path.getParent();
1071         if (parent != null && Files.notExists(parent))
1072             mkdirs(parent);
1073         return path;
1074     }
1075 }
1076