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