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