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 
  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 
 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 
 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                 e.printStackTrace();    // 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 
 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 
 422     static Object[][] getEntries() {
 423         Object[][] entries = new Object[10 + rdm.nextInt(20)][3];
 424         for (int i = 0; i < entries.length; i++) {
 425             entries[i][0] = "entries" + i;
 426             entries[i][1] = rdm.nextInt(10) % 2 == 0 ?
 427                 METHOD_STORED : METHOD_DEFLATED;
 428             entries[i][2] = new byte[rdm.nextInt(8192)];
 429             rdm.nextBytes((byte[])entries[i][2]);
 430         }
 431         return entries;
 432     }
 433 
 434     // check the content of read from zipfs is equal to the "bytes"
 435     private static void checkRead(Path path, byte[] expected) throws IOException {
 436 
 437         // fileAttribute
 438         CRC32 crc32 = new CRC32();
 439         crc32.update(expected);
 440 
 441         if (((Long)Files.getAttribute(path, "zip:crc")).intValue() !=
 442             (int)crc32.getValue()) {
 443             System.out.printf(" getAttribute.crc <%s> failed %x vs %x ...%n",
 444                               path.toString(),
 445                               ((Long)Files.getAttribute(path, "zip:crc")).intValue(),
 446                               (int)crc32.getValue());
 447             throw new RuntimeException("CHECK FAILED!");
 448         }
 449 
 450         if (((Long)Files.getAttribute(path, "zip:size")).intValue() != expected.length) {
 451             System.out.printf(" getAttribute.size <%s> failed %x vs %x ...%n",
 452                               path.toString(),
 453                               ((Long)Files.getAttribute(path, "zip:size")).intValue(),
 454                               expected.length);
 455             throw new RuntimeException("CHECK FAILED!");
 456         }
 457 
 458         //streams
 459         try (InputStream is = Files.newInputStream(path)) {
 460             if (!Arrays.equals(is.readAllBytes(), expected)) {
 461                 System.out.printf(" newInputStream <%s> failed...%n", path.toString());
 462                 throw new RuntimeException("CHECK FAILED!");
 463             }
 464         }
 465 
 466         // channels -- via sun.nio.ch.ChannelInputStream
 467         try (SeekableByteChannel sbc = Files.newByteChannel(path);
 468             InputStream is = Channels.newInputStream(sbc)) {
 469 
 470             // check all bytes match
 471             if (!Arrays.equals(is.readAllBytes(), expected)) {
 472                 System.out.printf(" newByteChannel <%s> failed...%n", path.toString());
 473                 throw new RuntimeException("CHECK FAILED!");
 474             }
 475 
 476             // Check if read position is at the end
 477             if (sbc.position() != expected.length) {
 478                 System.out.printf("pos [%s]: size=%d, position=%d%n",
 479                                   path.toString(), expected.length, sbc.position());
 480                 throw new RuntimeException("CHECK FAILED!");
 481             }
 482 
 483             // Check position(x) + read() at the random/specific pos/len
 484             byte[] buf = new byte[1024];
 485             ByteBuffer bb = ByteBuffer.wrap(buf);
 486             for (int i = 0; i < 10; i++) {
 487                 int pos = 0;
 488                 int len = 0;
 489                 if (expected.length > 0) {
 490                     pos = rdm.nextInt((int) sbc.size());
 491                     len = rdm.nextInt(Math.min(buf.length, expected.length - pos));
 492                 }
 493                 // System.out.printf("  --> %d, %d%n", pos, len);
 494                 bb.position(0).limit(len);    // bb.flip().limit(len);
 495                 if (sbc.position(pos).position() != pos ||
 496                     sbc.read(bb) != len ||
 497                     !Arrays.equals(buf, 0, bb.position(), expected, pos, pos + len)) {
 498                     System.out.printf("read()/position() failed%n");
 499                 }
 500             }
 501         } catch (IOException x) {
 502             x.printStackTrace();
 503             throw new RuntimeException("CHECK FAILED!");
 504         }
 505     }
 506 
 507     // test entry stream/channel reading
 508     static void testStreamChannel() throws Exception {
 509         Path zpath = getTempPath();
 510         try {
 511             var crc = new CRC32();
 512             Object[][] entries = getEntries();
 513 
 514             // [1] create zip via ZipOutputStream
 515             try (var os = Files.newOutputStream(zpath);
 516                  var zos = new ZipOutputStream(os)) {
 517                 for (Object[] entry : entries) {
 518                    var ze = new ZipEntry((String)entry[0]);
 519                    int method = (int)entry[1];
 520                    byte[] bytes = (byte[])entry[2];
 521                    if (method == METHOD_STORED) {
 522                        ze.setSize(bytes.length);
 523                        crc.reset();
 524                        crc.update(bytes);
 525                        ze.setCrc(crc.getValue());
 526                    }
 527                    ze.setMethod(method);
 528                    zos.putNextEntry(ze);
 529                    zos.write(bytes);
 530                    zos.closeEntry();
 531                 }
 532             }
 533             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
 534                 for (Object[] e : entries) {
 535                     Path path = zfs.getPath((String)e[0]);
 536                     int method = (int)e[1];
 537                     byte[] bytes = (byte[])e[2];
 538                     // System.out.printf("checking read [%s, %d, %d]%n",
 539                     //                   path.toString(), bytes.length, method);
 540                     checkRead(path, bytes);
 541                 }
 542             }
 543             Files.deleteIfExists(zpath);
 544 
 545             // [2] create zip via zfs.newByteChannel
 546             try (var zfs = newZipFileSystem(zpath, Map.of("create", "true"))) {
 547                 for (Object[] e : entries) {
 548                     //  tbd: method is not used
 549                     try (var sbc = Files.newByteChannel(zfs.getPath((String)e[0]),
 550                                                         CREATE_NEW, WRITE)) {
 551                         sbc.write(ByteBuffer.wrap((byte[])e[2]));
 552                     }
 553                 }
 554             }
 555             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
 556                 for (Object[] e : entries) {
 557                     checkRead(zfs.getPath((String)e[0]), (byte[])e[2]);
 558                 }
 559             }
 560             Files.deleteIfExists(zpath);
 561 
 562             // [3] create zip via Files.write()/newoutputStream/
 563             try (var zfs = newZipFileSystem(zpath, Map.of("create", "true"))) {
 564                 for (Object[] e : entries) {
 565                     Files.write(zfs.getPath((String)e[0]), (byte[])e[2]);
 566                 }
 567             }
 568             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
 569                 for (Object[] e : entries) {
 570                     checkRead(zfs.getPath((String)e[0]), (byte[])e[2]);
 571                 }
 572             }
 573             Files.deleteIfExists(zpath);
 574 
 575             // [4] create zip via zfs.newByteChannel, with "method_stored"
 576             try (var zfs = newZipFileSystem(zpath,
 577                     Map.of("create", true, "noCompression", true))) {
 578                 for (Object[] e : entries) {
 579                     try (var sbc = Files.newByteChannel(zfs.getPath((String)e[0]),
 580                                                         CREATE_NEW, WRITE)) {
 581                         sbc.write(ByteBuffer.wrap((byte[])e[2]));
 582                     }
 583                 }
 584             }
 585             try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) {
 586                 for (Object[] e : entries) {
 587                     checkRead(zfs.getPath((String)e[0]), (byte[])e[2]);
 588                 }
 589             }
 590             Files.deleteIfExists(zpath);
 591 
 592         } finally {
 593             Files.deleteIfExists(zpath);
 594         }
 595     }
 596 
 597     // test file stamp
 598     static void testTime(Path src) throws Exception {
 599         BasicFileAttributes attrs = Files
 600                         .getFileAttributeView(src, BasicFileAttributeView.class)
 601                         .readAttributes();
 602         // create a new filesystem, copy this file into it
 603         Map<String, Object> env = new HashMap<String, Object>();
 604         env.put("create", "true");
 605         Path fsPath = getTempPath();
 606         try (FileSystem fs = newZipFileSystem(fsPath, env)) {
 607             System.out.println("test copy with timestamps...");
 608             // copyin
 609             Path dst = getPathWithParents(fs, "me");
 610             Files.copy(src, dst, COPY_ATTRIBUTES);
 611             checkEqual(src, dst);
 612             System.out.println("mtime: " + attrs.lastModifiedTime());
 613             System.out.println("ctime: " + attrs.creationTime());
 614             System.out.println("atime: " + attrs.lastAccessTime());
 615             System.out.println(" ==============>");
 616             BasicFileAttributes dstAttrs = Files
 617                             .getFileAttributeView(dst, BasicFileAttributeView.class)
 618                             .readAttributes();
 619             System.out.println("mtime: " + dstAttrs.lastModifiedTime());
 620             System.out.println("ctime: " + dstAttrs.creationTime());
 621             System.out.println("atime: " + dstAttrs.lastAccessTime());
 622 
 623             // 1-second granularity
 624             if (attrs.lastModifiedTime().to(TimeUnit.SECONDS) !=
 625                 dstAttrs.lastModifiedTime().to(TimeUnit.SECONDS) ||
 626                 attrs.lastAccessTime().to(TimeUnit.SECONDS) !=
 627                 dstAttrs.lastAccessTime().to(TimeUnit.SECONDS) ||
 628                 attrs.creationTime().to(TimeUnit.SECONDS) !=
 629                 dstAttrs.creationTime().to(TimeUnit.SECONDS)) {
 630                 throw new RuntimeException("Timestamp Copy Failed!");
 631             }
 632         } finally {
 633             Files.delete(fsPath);
 634         }
 635     }
 636 
 637     static void test8069211() throws Exception {
 638         // create a new filesystem, copy this file into it
 639         Map<String, Object> env = new HashMap<String, Object>();
 640         env.put("create", "true");
 641         Path fsPath = getTempPath();
 642         try (FileSystem fs = newZipFileSystem(fsPath, env);) {
 643             OutputStream out = Files.newOutputStream(fs.getPath("/foo"));
 644             out.write("hello".getBytes());
 645             out.close();
 646             out.close();
 647         }
 648         try (FileSystem fs = newZipFileSystem(fsPath, new HashMap<String, Object>())) {
 649             if (!Arrays.equals(Files.readAllBytes(fs.getPath("/foo")),
 650                                "hello".getBytes())) {
 651                 throw new RuntimeException("entry close() failed");
 652             }
 653         } catch (Exception x) {
 654             throw new RuntimeException("entry close() failed", x);
 655         } finally {
 656             Files.delete(fsPath);
 657         }
 658     }
 659 
 660     static void test8131067() throws Exception {
 661         Map<String, Object> env = new HashMap<String, Object>();
 662         env.put("create", "true");
 663 
 664         // file name with space character for URI to quote it
 665         File tmp = File.createTempFile("test zipfs", "zip");
 666         tmp.delete();    // we need a clean path, no file
 667         Path fsPath = tmp.toPath();
 668         try (FileSystem fs = newZipFileSystem(fsPath, env);) {
 669             Files.write(fs.getPath("/foo"), "hello".getBytes());
 670             URI fooUri = fs.getPath("/foo").toUri();
 671             if (!Arrays.equals(Files.readAllBytes(Paths.get(fooUri)),
 672                                "hello".getBytes())) {
 673                 throw new RuntimeException("entry close() failed");
 674             }
 675         } finally {
 676             Files.delete(fsPath);
 677         }
 678     }
 679 
 680     private static FileSystem newZipFileSystem(Path path, Map<String, ?> env)
 681         throws Exception
 682     {
 683         // Use URLDecoder (for test only) to remove the double escaped space
 684         // character
 685         return FileSystems.newFileSystem(
 686             new URI("jar", URLDecoder.decode(path.toUri().toString(), "utf8"),
 687                 null), env, null);
 688     }
 689 
 690     private static Path getTempPath() throws IOException
 691     {
 692         File tmp = File.createTempFile("testzipfs_", "zip");
 693         tmp.delete();    // we need a clean path, no file
 694         return tmp.toPath();
 695     }
 696 
 697     private static void list(Path path, List<String> files, List<String> dirs )
 698         throws IOException
 699     {
 700         if (Files.isDirectory(path)) {
 701             try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
 702                 for (Path child : ds)
 703                     list(child, files, dirs);
 704             }
 705             dirs.add(path.toString());
 706         } else {
 707             files.add(path.toString());
 708         }
 709     }
 710 
 711     private static void z2zcopy(FileSystem src, FileSystem dst, String path,
 712                                 int method)
 713         throws IOException
 714     {
 715         Path srcPath = src.getPath(path);
 716         Path dstPath = dst.getPath(path);
 717 
 718         if (Files.isDirectory(srcPath)) {
 719             if (!Files.exists(dstPath)) {
 720                 try {
 721                     mkdirs(dstPath);
 722                 } catch (FileAlreadyExistsException x) {}
 723             }
 724             try (DirectoryStream<Path> ds = Files.newDirectoryStream(srcPath)) {
 725                 for (Path child : ds) {
 726                     z2zcopy(src, dst,
 727                            path + (path.endsWith("/")?"":"/") + child.getFileName(),
 728                            method);
 729                 }
 730             }
 731         } else {
 732             try {
 733                 if (Files.exists(dstPath))
 734                     return;
 735                 switch (method) {
 736                 case 0:
 737                     Files.copy(srcPath, dstPath);
 738                     break;
 739                 case 1:
 740                     chCopy(srcPath, dstPath);
 741                     break;
 742                 case 2:
 743                     //fchCopy(srcPath, dstPath);
 744                     streamCopy(srcPath, dstPath);
 745                     break;
 746                 }
 747             } catch (FileAlreadyExistsException x) {}
 748         }
 749     }
 750 
 751     private static void z2zmove(FileSystem src, FileSystem dst, String path)
 752         throws IOException
 753     {
 754         Path srcPath = src.getPath(path);
 755         Path dstPath = dst.getPath(path);
 756 
 757         if (Files.isDirectory(srcPath)) {
 758             if (!Files.exists(dstPath))
 759                 mkdirs(dstPath);
 760             try (DirectoryStream<Path> ds = Files.newDirectoryStream(srcPath)) {
 761                 for (Path child : ds) {
 762                     z2zmove(src, dst,
 763                             path + (path.endsWith("/")?"":"/") + child.getFileName());
 764                 }
 765             }
 766         } else {
 767             //System.out.println("moving..." + path);
 768             Path parent = dstPath.getParent();
 769             if (parent != null && Files.notExists(parent))
 770                 mkdirs(parent);
 771             Files.move(srcPath, dstPath);
 772         }
 773     }
 774 
 775     private static void walk(Path path) throws IOException
 776     {
 777         Files.walkFileTree(
 778             path,
 779             new SimpleFileVisitor<Path>() {
 780                 private int indent = 0;
 781                 private void indent() {
 782                     int n = 0;
 783                     while (n++ < indent)
 784                         System.out.printf(" ");
 785                 }
 786 
 787                 @Override
 788                 public FileVisitResult visitFile(Path file,
 789                                                  BasicFileAttributes attrs)
 790                 {
 791                     indent();
 792                     System.out.printf("%s%n", file.getFileName().toString());
 793                     return FileVisitResult.CONTINUE;
 794                 }
 795 
 796                 @Override
 797                 public FileVisitResult preVisitDirectory(Path dir,
 798                                                          BasicFileAttributes attrs)
 799                 {
 800                     indent();
 801                     System.out.printf("[%s]%n", dir.toString());
 802                     indent += 2;
 803                     return FileVisitResult.CONTINUE;
 804                 }
 805 
 806                 @Override
 807                 public FileVisitResult postVisitDirectory(Path dir,
 808                                                           IOException ioe)
 809                     throws IOException
 810                 {
 811                     indent -= 2;
 812                     return FileVisitResult.CONTINUE;
 813                 }
 814         });
 815     }
 816 
 817     private static void mkdirs(Path path) throws IOException {
 818         if (Files.exists(path))
 819             return;
 820         path = path.toAbsolutePath();
 821         Path parent = path.getParent();
 822         if (parent != null) {
 823             if (Files.notExists(parent))
 824                 mkdirs(parent);
 825         }
 826         Files.createDirectory(path);
 827     }
 828 
 829     private static void rmdirs(Path path) throws IOException {
 830         while (path != null && path.getNameCount() != 0) {
 831             Files.delete(path);
 832             path = path.getParent();
 833         }
 834     }
 835 
 836     // check the content of two paths are equal
 837     private static void checkEqual(Path src, Path dst) throws IOException
 838     {
 839         System.out.printf("checking <%s> vs <%s>...%n",
 840                           src.toString(), dst.toString());
 841 
 842         //streams
 843         byte[] bufSrc = new byte[8192];
 844         byte[] bufDst = new byte[8192];
 845         try (InputStream isSrc = Files.newInputStream(src);
 846              InputStream isDst = Files.newInputStream(dst))
 847         {
 848             int nSrc = 0;
 849             while ((nSrc = isSrc.read(bufSrc)) != -1) {
 850                 int nDst = 0;
 851                 while (nDst < nSrc) {
 852                     int n = isDst.read(bufDst, nDst, nSrc - nDst);
 853                     if (n == -1) {
 854                         System.out.printf("checking <%s> vs <%s>...%n",
 855                                           src.toString(), dst.toString());
 856                         throw new RuntimeException("CHECK FAILED!");
 857                     }
 858                     nDst += n;
 859                 }
 860                 while (--nSrc >= 0) {
 861                     if (bufSrc[nSrc] != bufDst[nSrc]) {
 862                         System.out.printf("checking <%s> vs <%s>...%n",
 863                                           src.toString(), dst.toString());
 864                         throw new RuntimeException("CHECK FAILED!");
 865                     }
 866                     nSrc--;
 867                 }
 868             }
 869         }
 870 
 871         // channels
 872         try (SeekableByteChannel chSrc = Files.newByteChannel(src);
 873              SeekableByteChannel chDst = Files.newByteChannel(dst))
 874         {
 875             if (chSrc.size() != chDst.size()) {
 876                 System.out.printf("src[%s].size=%d, dst[%s].size=%d%n",
 877                                   chSrc.toString(), chSrc.size(),
 878                                   chDst.toString(), chDst.size());
 879                 throw new RuntimeException("CHECK FAILED!");
 880             }
 881             ByteBuffer bbSrc = ByteBuffer.allocate(8192);
 882             ByteBuffer bbDst = ByteBuffer.allocate(8192);
 883 
 884             int nSrc = 0;
 885             while ((nSrc = chSrc.read(bbSrc)) != -1) {
 886                 int nDst = chDst.read(bbDst);
 887                 if (nSrc != nDst) {
 888                     System.out.printf("checking <%s> vs <%s>...%n",
 889                                       src.toString(), dst.toString());
 890                     throw new RuntimeException("CHECK FAILED!");
 891                 }
 892                 while (--nSrc >= 0) {
 893                     if (bbSrc.get(nSrc) != bbDst.get(nSrc)) {
 894                         System.out.printf("checking <%s> vs <%s>...%n",
 895                                           src.toString(), dst.toString());
 896                         throw new RuntimeException("CHECK FAILED!");
 897                     }
 898                     nSrc--;
 899                 }
 900                 bbSrc.flip();
 901                 bbDst.flip();
 902             }
 903 
 904             // Check if source read position is at the end
 905             if (chSrc.position() != chSrc.size()) {
 906                 System.out.printf("src[%s]: size=%d, position=%d%n",
 907                                   chSrc.toString(), chSrc.size(), chSrc.position());
 908                 throw new RuntimeException("CHECK FAILED!");
 909             }
 910 
 911             // Check if destination read position is at the end
 912             if (chDst.position() != chDst.size()) {
 913                 System.out.printf("dst[%s]: size=%d, position=%d%n",
 914                                   chDst.toString(), chDst.size(), chDst.position());
 915                 throw new RuntimeException("CHECK FAILED!");
 916             }
 917 
 918             // Check position(x) + read() at the specific pos/len
 919             for (int i = 0; i < 10; i++) {
 920                 int pos = rdm.nextInt((int)chSrc.size());
 921                 int limit = rdm.nextInt(1024);
 922                 if (chSrc.position(pos).position() != chDst.position(pos).position()) {
 923                     System.out.printf("dst/src.position(pos failed%n");
 924                 }
 925                 bbSrc.clear().limit(limit);
 926                 bbDst.clear().limit(limit);
 927                 if (chSrc.read(bbSrc) != chDst.read(bbDst) ||
 928                     !bbSrc.flip().equals(bbDst.flip())) {
 929                     System.out.printf("dst/src.read() failed%n");
 930                 }
 931             }
 932         } catch (IOException x) {
 933             x.printStackTrace();
 934         }
 935     }
 936 
 937     private static void fchCopy(Path src, Path dst) throws IOException
 938     {
 939         Set<OpenOption> read = new HashSet<>();
 940         read.add(READ);
 941         Set<OpenOption> openwrite = new HashSet<>();
 942         openwrite.add(CREATE_NEW);
 943         openwrite.add(WRITE);
 944 
 945         try (FileChannel srcFc = src.getFileSystem()
 946                                     .provider()
 947                                     .newFileChannel(src, read);
 948              FileChannel dstFc = dst.getFileSystem()
 949                                     .provider()
 950                                     .newFileChannel(dst, openwrite))
 951         {
 952             ByteBuffer bb = ByteBuffer.allocate(8192);
 953             while (srcFc.read(bb) >= 0) {
 954                 bb.flip();
 955                 dstFc.write(bb);
 956                 bb.clear();
 957             }
 958         }
 959     }
 960 
 961     private static void chCopy(Path src, Path dst) throws IOException
 962     {
 963         Set<OpenOption> read = new HashSet<>();
 964         read.add(READ);
 965         Set<OpenOption> openwrite = new HashSet<>();
 966         openwrite.add(CREATE_NEW);
 967         openwrite.add(WRITE);
 968 
 969         try (SeekableByteChannel srcCh = Files.newByteChannel(src, read);
 970              SeekableByteChannel dstCh = Files.newByteChannel(dst, openwrite))
 971         {
 972 
 973             ByteBuffer bb = ByteBuffer.allocate(8192);
 974             while (srcCh.read(bb) >= 0) {
 975                 bb.flip();
 976                 dstCh.write(bb);
 977                 bb.clear();
 978             }
 979 
 980             // Check if source read position is at the end
 981             if (srcCh.position() != srcCh.size()) {
 982                 System.out.printf("src[%s]: size=%d, position=%d%n",
 983                                   srcCh.toString(), srcCh.size(), srcCh.position());
 984                 throw new RuntimeException("CHECK FAILED!");
 985             }
 986 
 987             // Check if destination write position is at the end
 988             if (dstCh.position() != dstCh.size()) {
 989                 System.out.printf("dst[%s]: size=%d, position=%d%n",
 990                                   dstCh.toString(), dstCh.size(), dstCh.position());
 991                 throw new RuntimeException("CHECK FAILED!");
 992             }
 993         }
 994     }
 995 
 996     private static void streamCopy(Path src, Path dst) throws IOException
 997     {
 998         byte[] buf = new byte[8192];
 999         try (InputStream isSrc = Files.newInputStream(src);
1000              OutputStream osDst = Files.newOutputStream(dst))
1001         {
1002             int n = 0;
1003             while ((n = isSrc.read(buf)) != -1) {
1004                 osDst.write(buf, 0, n);
1005             }
1006         }
1007     }
1008 
1009     static void channel(FileSystem fs, Path path)
1010         throws Exception
1011     {
1012         System.out.println("test ByteChannel...");
1013         Set<OpenOption> read = new HashSet<>();
1014         read.add(READ);
1015         int n = 0;
1016         ByteBuffer bb = null;
1017         ByteBuffer bb2 = null;
1018         int N = 120;
1019 
1020         try (SeekableByteChannel sbc = Files.newByteChannel(path)) {
1021             System.out.printf("   sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size());
1022             if (sbc.position() != 0) {
1023                 throw new RuntimeException("CHECK FAILED!");
1024             }
1025 
1026             bb = ByteBuffer.allocate((int)sbc.size());
1027             n = sbc.read(bb);
1028             System.out.printf("   sbc[1]: read=%d, pos=%d, size=%d%n",
1029                               n, sbc.position(), sbc.size());
1030             if (sbc.position() != sbc.size()) {
1031                 throw new RuntimeException("CHECK FAILED!");
1032             }
1033             bb2 = ByteBuffer.allocate((int)sbc.size());
1034         }
1035 
1036         // sbc.position(pos) is not supported in current version
1037         // try the FileChannel
1038         try (SeekableByteChannel sbc = fs.provider().newFileChannel(path, read)) {
1039             sbc.position(N);
1040             System.out.printf("   sbc[2]: pos=%d, size=%d%n",
1041                               sbc.position(), sbc.size());
1042             if (sbc.position() != N) {
1043                 throw new RuntimeException("CHECK FAILED!");
1044             }
1045             bb2.limit(100);
1046             n = sbc.read(bb2);
1047             System.out.printf("   sbc[3]: read=%d, pos=%d, size=%d%n",
1048                               n, sbc.position(), sbc.size());
1049             if (n < 0 || sbc.position() != (N + n)) {
1050                 throw new RuntimeException("CHECK FAILED!");
1051             }
1052             System.out.printf("   sbc[4]: bb[%d]=%d, bb1[0]=%d%n",
1053                               N, bb.get(N) & 0xff, bb2.get(0) & 0xff);
1054         }
1055     }
1056 
1057     // create parents if does not exist
1058     static Path getPathWithParents(FileSystem fs, String name)
1059         throws Exception
1060     {
1061         Path path = fs.getPath(name);
1062         Path parent = path.getParent();
1063         if (parent != null && Files.notExists(parent))
1064             mkdirs(parent);
1065         return path;
1066     }
1067 }