1 /*
   2  * Copyright (c) 2010, 2015, 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.FileChannel;
  32 import java.nio.channels.SeekableByteChannel;
  33 import java.nio.file.DirectoryStream;
  34 import java.nio.file.FileAlreadyExistsException;
  35 import java.nio.file.FileSystem;
  36 import java.nio.file.FileSystemAlreadyExistsException;
  37 import java.nio.file.FileSystemException;
  38 import java.nio.file.FileSystems;
  39 import java.nio.file.FileVisitResult;
  40 import java.nio.file.Files;
  41 import java.nio.file.OpenOption;
  42 import java.nio.file.Path;
  43 import java.nio.file.Paths;
  44 import java.nio.file.SimpleFileVisitor;
  45 import java.nio.file.attribute.BasicFileAttributeView;
  46 import java.nio.file.attribute.BasicFileAttributes;
  47 import java.nio.file.spi.FileSystemProvider;
  48 import java.util.ArrayList;
  49 import java.util.Arrays;
  50 import java.util.Collections;
  51 import java.util.Enumeration;
  52 import java.util.HashMap;
  53 import java.util.HashSet;
  54 import java.util.Iterator;
  55 import java.util.LinkedList;
  56 import java.util.List;
  57 import java.util.Map;
  58 import java.util.Random;
  59 import java.util.Set;
  60 import java.util.concurrent.TimeUnit;
  61 import java.util.zip.ZipEntry;
  62 import java.util.zip.ZipFile;
  63 
  64 import static java.nio.file.StandardOpenOption.*;
  65 import static java.nio.file.StandardCopyOption.*;
  66 
  67 /*
  68  * Tests various zipfs operations.
  69  *
  70  * @test
  71  * @bug 6990846 7009092 7009085 7015391 7014948 7005986 7017840 7007596
  72  *      7157656 8002390 7012868 7012856 8015728 8038500 8040059 8069211
  73  *      8131067
  74  * @summary Test Zip filesystem provider
  75  * @run main ZipFSTester
  76  * @run main/othervm/java.security.policy=test.policy ZipFSTester
  77  */
  78 
  79 public class ZipFSTester {
  80 
  81     public static void main(String[] args) throws Exception {
  82 
  83         // create JAR file for test, actual contents don't matter
  84         Path jarFile = Utils.createJarFile("tester.jar",
  85                 "META-INF/MANIFEST.MF",
  86                 "dir1/foo",
  87                 "dir2/bar");
  88 
  89         try (FileSystem fs = newZipFileSystem(jarFile, Collections.emptyMap())) {
  90             test0(fs);
  91             test1(fs);
  92             test2(fs);   // more tests
  93         }
  94         testTime(jarFile);
  95         test8069211();
  96         test8131067();
  97     }
  98 
  99     static void test0(FileSystem fs)
 100         throws Exception
 101     {
 102         List<String> list = new LinkedList<>();
 103         try (ZipFile zf = new ZipFile(fs.toString())) {
 104             Enumeration<? extends ZipEntry> zes = zf.entries();
 105             while (zes.hasMoreElements()) {
 106                 list.add(zes.nextElement().getName());
 107             }
 108             for (String pname : list) {
 109                 Path path = fs.getPath(pname);
 110                 if (!Files.exists(path))
 111                     throw new RuntimeException("path existence check failed!");
 112                 while ((path = path.getParent()) != null) {
 113                     if (!Files.exists(path))
 114                         throw new RuntimeException("parent existence check failed!");
 115                 }
 116             }
 117         }
 118     }
 119 
 120     static void test1(FileSystem fs0)
 121         throws Exception
 122     {
 123         Random rdm = new Random();
 124         // clone a fs and test on it
 125         Path tmpfsPath = getTempPath();
 126         Map<String, Object> env = new HashMap<String, Object>();
 127         env.put("create", "true");
 128         try (FileSystem copy = newZipFileSystem(tmpfsPath, env)) {
 129             z2zcopy(fs0, copy, "/", 0);
 130         }
 131 
 132         try (FileSystem fs = newZipFileSystem(tmpfsPath, new HashMap<String, Object>())) {
 133 
 134             FileSystemProvider provider = fs.provider();
 135             // newFileSystem(path...) should not throw exception
 136             try (FileSystem fsPath = provider.newFileSystem(tmpfsPath, new HashMap<String, Object>())){}
 137             try (FileSystem fsUri = provider.newFileSystem(
 138                      new URI("jar", tmpfsPath.toUri().toString(), null),
 139                      new HashMap<String, Object>()))
 140             {
 141                 throw new RuntimeException("newFileSystem(URI...) does not throw exception");
 142             } catch (FileSystemAlreadyExistsException fsaee) {}
 143 
 144             // prepare a src
 145             Path src = getTempPath();
 146             String tmpName = src.toString();
 147             OutputStream os = Files.newOutputStream(src);
 148             byte[] bits = new byte[12345];
 149             rdm.nextBytes(bits);
 150             os.write(bits);
 151             os.close();
 152 
 153             try {
 154                 provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(),
 155                                        new HashMap<String, Object>());
 156                 throw new RuntimeException("newFileSystem() opens a directory as zipfs");
 157             } catch (UnsupportedOperationException uoe) {}
 158 
 159             try {
 160                 provider.newFileSystem(src, new HashMap<String, Object>());
 161                 throw new RuntimeException("newFileSystem() opens a non-zip file as zipfs");
 162             } catch (UnsupportedOperationException uoe) {}
 163 
 164 
 165             // copyin
 166             Path dst = getPathWithParents(fs, tmpName);
 167             Files.copy(src, dst);
 168             checkEqual(src, dst);
 169 
 170             // copy
 171             Path dst2 = getPathWithParents(fs, "/xyz" + rdm.nextInt(100) +
 172                                            "/efg" + rdm.nextInt(100) + "/foo.class");
 173             Files.copy(dst, dst2);
 174             //dst.moveTo(dst2);
 175             checkEqual(src, dst2);
 176 
 177             // delete
 178             Files.delete(dst);
 179             if (Files.exists(dst))
 180                 throw new RuntimeException("Failed!");
 181 
 182             // moveout
 183             Path dst3 = Paths.get(tmpName + "_Tmp");
 184             Files.move(dst2, dst3);
 185             checkEqual(src, dst3);
 186             if (Files.exists(dst2))
 187                 throw new RuntimeException("Failed!");
 188 
 189             // copyback + move
 190             Files.copy(dst3, dst);
 191             Path dst4 = getPathWithParents(fs, tmpName + "_Tmp0");
 192             Files.move(dst, dst4);
 193             checkEqual(src, dst4);
 194 
 195             // delete
 196             Files.delete(dst4);
 197             if (Files.exists(dst4))
 198                 throw new RuntimeException("Failed!");
 199             Files.delete(dst3);
 200             if (Files.exists(dst3))
 201                 throw new RuntimeException("Failed!");
 202 
 203             // move (existing entry)
 204             Path dst5 = fs.getPath("META-INF/MANIFEST.MF");
 205             if (Files.exists(dst5)) {
 206                 Path dst6 = fs.getPath("META-INF/MANIFEST.MF_TMP");
 207                 Files.move(dst5, dst6);
 208                 walk(fs.getPath("/"));
 209             }
 210 
 211             // newInputStream on dir
 212             Path parent = dst2.getParent();
 213             try {
 214                 Files.newInputStream(parent);
 215                 throw new RuntimeException("Failed");
 216             } catch (FileSystemException e) {
 217                 e.printStackTrace();    // expected fse
 218             }
 219 
 220             // rmdirs
 221             try {
 222                 rmdirs(parent);
 223             } catch (IOException x) {
 224                 x.printStackTrace();
 225             }
 226 
 227             // newFileChannel() copy in, out and verify via fch
 228             fchCopy(src, dst);    // in
 229             checkEqual(src, dst);
 230             Path tmp = Paths.get(tmpName + "_Tmp");
 231             fchCopy(dst, tmp);   //  out
 232             checkEqual(src, tmp);
 233             Files.delete(tmp);
 234 
 235             // test channels
 236             channel(fs, dst);
 237             Files.delete(dst);
 238             Files.delete(src);
 239         } finally {
 240             if (Files.exists(tmpfsPath))
 241                 Files.delete(tmpfsPath);
 242         }
 243     }
 244 
 245     static void test2(FileSystem fs) throws Exception {
 246 
 247         Path fs1Path = getTempPath();
 248         Path fs2Path = getTempPath();
 249         Path fs3Path = getTempPath();
 250 
 251         // create a new filesystem, copy everything from fs
 252         Map<String, Object> env = new HashMap<String, Object>();
 253         env.put("create", "true");
 254         FileSystem fs0 = newZipFileSystem(fs1Path, env);
 255 
 256         final FileSystem fs2 = newZipFileSystem(fs2Path, env);
 257         final FileSystem fs3 = newZipFileSystem(fs3Path, env);
 258 
 259         System.out.println("copy src: fs -> fs0...");
 260         z2zcopy(fs, fs0, "/", 0);   // copy fs -> fs1
 261         fs0.close();                // dump to file
 262 
 263         System.out.println("open fs0 as fs1");
 264         env = new HashMap<String, Object>();
 265         final FileSystem fs1 = newZipFileSystem(fs1Path, env);
 266 
 267         System.out.println("listing...");
 268         final ArrayList<String> files = new ArrayList<>();
 269         final ArrayList<String> dirs = new ArrayList<>();
 270         list(fs1.getPath("/"), files, dirs);
 271 
 272         Thread t0 = new Thread(new Runnable() {
 273             public void run() {
 274                 List<String> list = new ArrayList<>(dirs);
 275                 Collections.shuffle(list);
 276                 for (String path : list) {
 277                     try {
 278                         z2zcopy(fs1, fs2, path, 0);
 279                     } catch (Exception x) {
 280                         x.printStackTrace();
 281                     }
 282                 }
 283             }
 284 
 285         });
 286 
 287         Thread t1 = new Thread(new Runnable() {
 288             public void run() {
 289                 List<String> list = new ArrayList<>(dirs);
 290                 Collections.shuffle(list);
 291                 for (String path : list) {
 292                     try {
 293                         z2zcopy(fs1, fs2, path, 1);
 294                     } catch (Exception x) {
 295                         x.printStackTrace();
 296                     }
 297                 }
 298             }
 299 
 300         });
 301 
 302         Thread t2 = new Thread(new Runnable() {
 303             public void run() {
 304                 List<String> list = new ArrayList<>(dirs);
 305                 Collections.shuffle(list);
 306                 for (String path : list) {
 307                     try {
 308                         z2zcopy(fs1, fs2, path, 2);
 309                     } catch (Exception x) {
 310                         x.printStackTrace();
 311                     }
 312                 }
 313             }
 314 
 315         });
 316 
 317         Thread t3 = new Thread(new Runnable() {
 318             public void run() {
 319                 List<String> list = new ArrayList<>(files);
 320                 Collections.shuffle(list);
 321                 while (!list.isEmpty()) {
 322                     Iterator<String> itr = list.iterator();
 323                     while (itr.hasNext()) {
 324                         String path = itr.next();
 325                         try {
 326                             if (Files.exists(fs2.getPath(path))) {
 327                                 z2zmove(fs2, fs3, path);
 328                                 itr.remove();
 329                             }
 330                         } catch (FileAlreadyExistsException x){
 331                             itr.remove();
 332                         } catch (Exception x) {
 333                             x.printStackTrace();
 334                         }
 335                     }
 336                 }
 337             }
 338 
 339         });
 340 
 341         System.out.println("copying/removing...");
 342         t0.start(); t1.start(); t2.start(); t3.start();
 343         t0.join(); t1.join(); t2.join(); t3.join();
 344 
 345         System.out.println("closing: fs1, fs2");
 346         fs1.close();
 347         fs2.close();
 348 
 349         int failed = 0;
 350         System.out.println("checkEqual: fs vs fs3");
 351         for (String path : files) {
 352             try {
 353                 checkEqual(fs.getPath(path), fs3.getPath(path));
 354             } catch (IOException x) {
 355                 //x.printStackTrace();
 356                 failed++;
 357             }
 358         }
 359         System.out.println("closing: fs3");
 360         fs3.close();
 361 
 362         System.out.println("opening: fs3 as fs4");
 363         FileSystem fs4 = newZipFileSystem(fs3Path, env);
 364 
 365 
 366         ArrayList<String> files2 = new ArrayList<>();
 367         ArrayList<String> dirs2 = new ArrayList<>();
 368         list(fs4.getPath("/"), files2, dirs2);
 369 
 370         System.out.println("checkEqual: fs vs fs4");
 371         for (String path : files2) {
 372             checkEqual(fs.getPath(path), fs4.getPath(path));
 373         }
 374         System.out.println("walking: fs4");
 375         walk(fs4.getPath("/"));
 376         System.out.println("closing: fs4");
 377         fs4.close();
 378         System.out.printf("failed=%d%n", failed);
 379 
 380         Files.delete(fs1Path);
 381         Files.delete(fs2Path);
 382         Files.delete(fs3Path);
 383     }
 384 
 385     // test file stamp
 386     static void testTime(Path src) throws Exception {
 387         BasicFileAttributes attrs = Files
 388                         .getFileAttributeView(src, BasicFileAttributeView.class)
 389                         .readAttributes();
 390         // create a new filesystem, copy this file into it
 391         Map<String, Object> env = new HashMap<String, Object>();
 392         env.put("create", "true");
 393         Path fsPath = getTempPath();
 394         FileSystem fs = newZipFileSystem(fsPath, env);
 395 
 396         System.out.println("test copy with timestamps...");
 397         // copyin
 398         Path dst = getPathWithParents(fs, "me");
 399         Files.copy(src, dst, COPY_ATTRIBUTES);
 400         checkEqual(src, dst);
 401         System.out.println("mtime: " + attrs.lastModifiedTime());
 402         System.out.println("ctime: " + attrs.creationTime());
 403         System.out.println("atime: " + attrs.lastAccessTime());
 404         System.out.println(" ==============>");
 405         BasicFileAttributes dstAttrs = Files
 406                         .getFileAttributeView(dst, BasicFileAttributeView.class)
 407                         .readAttributes();
 408         System.out.println("mtime: " + dstAttrs.lastModifiedTime());
 409         System.out.println("ctime: " + dstAttrs.creationTime());
 410         System.out.println("atime: " + dstAttrs.lastAccessTime());
 411 
 412         // 1-second granularity
 413         if (attrs.lastModifiedTime().to(TimeUnit.SECONDS) !=
 414             dstAttrs.lastModifiedTime().to(TimeUnit.SECONDS) ||
 415             attrs.lastAccessTime().to(TimeUnit.SECONDS) !=
 416             dstAttrs.lastAccessTime().to(TimeUnit.SECONDS) ||
 417             attrs.creationTime().to(TimeUnit.SECONDS) !=
 418             dstAttrs.creationTime().to(TimeUnit.SECONDS)) {
 419             throw new RuntimeException("Timestamp Copy Failed!");
 420         }
 421         Files.delete(fsPath);
 422     }
 423 
 424     static void test8069211() throws Exception {
 425         // create a new filesystem, copy this file into it
 426         Map<String, Object> env = new HashMap<String, Object>();
 427         env.put("create", "true");
 428         Path fsPath = getTempPath();
 429         try (FileSystem fs = newZipFileSystem(fsPath, env);) {
 430             OutputStream out = Files.newOutputStream(fs.getPath("/foo"));
 431             out.write("hello".getBytes());
 432             out.close();
 433             out.close();
 434         }
 435         try (FileSystem fs = newZipFileSystem(fsPath, new HashMap<String, Object>())) {
 436             if (!Arrays.equals(Files.readAllBytes(fs.getPath("/foo")),
 437                                "hello".getBytes())) {
 438                 throw new RuntimeException("entry close() failed");
 439             }
 440         } catch (Exception x) {
 441             throw new RuntimeException("entry close() failed", x);
 442         } finally {
 443             Files.delete(fsPath);
 444         }
 445     }
 446 
 447     static void test8131067() throws Exception {
 448         Map<String, Object> env = new HashMap<String, Object>();
 449         env.put("create", "true");
 450 
 451         // file name with space character for URI to quote it
 452         File tmp = File.createTempFile("test zipfs", "zip");
 453         tmp.delete();    // we need a clean path, no file
 454         Path fsPath = tmp.toPath();
 455         try (FileSystem fs = newZipFileSystem(fsPath, env);) {
 456             Files.write(fs.getPath("/foo"), "hello".getBytes());
 457             URI fooUri = fs.getPath("/foo").toUri();
 458             if (!Arrays.equals(Files.readAllBytes(Paths.get(fooUri)),
 459                                "hello".getBytes())) {
 460                 throw new RuntimeException("entry close() failed");
 461             }
 462         } finally {
 463             Files.delete(fsPath);
 464         }
 465     }
 466 
 467     private static FileSystem newZipFileSystem(Path path, Map<String, ?> env)
 468         throws Exception
 469     {
 470         // Use URLDecoder (for test only) to remove the double escaped space
 471         // character
 472         return FileSystems.newFileSystem(
 473             new URI("jar", URLDecoder.decode(path.toUri().toString(), "utf8"),
 474                 null), env, null);
 475     }
 476 
 477     private static Path getTempPath() throws IOException
 478     {
 479         File tmp = File.createTempFile("testzipfs_", "zip");
 480         tmp.delete();    // we need a clean path, no file
 481         return tmp.toPath();
 482     }
 483 
 484     private static void list(Path path, List<String> files, List<String> dirs )
 485         throws IOException
 486     {
 487         if (Files.isDirectory(path)) {
 488             try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
 489                 for (Path child : ds)
 490                     list(child, files, dirs);
 491             }
 492             dirs.add(path.toString());
 493         } else {
 494             files.add(path.toString());
 495         }
 496     }
 497 
 498     private static void z2zcopy(FileSystem src, FileSystem dst, String path,
 499                                 int method)
 500         throws IOException
 501     {
 502         Path srcPath = src.getPath(path);
 503         Path dstPath = dst.getPath(path);
 504 
 505         if (Files.isDirectory(srcPath)) {
 506             if (!Files.exists(dstPath)) {
 507                 try {
 508                     mkdirs(dstPath);
 509                 } catch (FileAlreadyExistsException x) {}
 510             }
 511             try (DirectoryStream<Path> ds = Files.newDirectoryStream(srcPath)) {
 512                 for (Path child : ds) {
 513                     z2zcopy(src, dst,
 514                            path + (path.endsWith("/")?"":"/") + child.getFileName(),
 515                            method);
 516                 }
 517             }
 518         } else {
 519             try {
 520                 if (Files.exists(dstPath))
 521                     return;
 522                 switch (method) {
 523                 case 0:
 524                     Files.copy(srcPath, dstPath);
 525                     break;
 526                 case 1:
 527                     chCopy(srcPath, dstPath);
 528                     break;
 529                 case 2:
 530                     //fchCopy(srcPath, dstPath);
 531                     streamCopy(srcPath, dstPath);
 532                     break;
 533                 }
 534             } catch (FileAlreadyExistsException x) {}
 535         }
 536     }
 537 
 538     private static void z2zmove(FileSystem src, FileSystem dst, String path)
 539         throws IOException
 540     {
 541         Path srcPath = src.getPath(path);
 542         Path dstPath = dst.getPath(path);
 543 
 544         if (Files.isDirectory(srcPath)) {
 545             if (!Files.exists(dstPath))
 546                 mkdirs(dstPath);
 547             try (DirectoryStream<Path> ds = Files.newDirectoryStream(srcPath)) {
 548                 for (Path child : ds) {
 549                     z2zmove(src, dst,
 550                             path + (path.endsWith("/")?"":"/") + child.getFileName());
 551                 }
 552             }
 553         } else {
 554             //System.out.println("moving..." + path);
 555             Path parent = dstPath.getParent();
 556             if (parent != null && Files.notExists(parent))
 557                 mkdirs(parent);
 558             Files.move(srcPath, dstPath);
 559         }
 560     }
 561 
 562     private static void walk(Path path) throws IOException
 563     {
 564         Files.walkFileTree(
 565             path,
 566             new SimpleFileVisitor<Path>() {
 567                 private int indent = 0;
 568                 private void indent() {
 569                     int n = 0;
 570                     while (n++ < indent)
 571                         System.out.printf(" ");
 572                 }
 573 
 574                 @Override
 575                 public FileVisitResult visitFile(Path file,
 576                                                  BasicFileAttributes attrs)
 577                 {
 578                     indent();
 579                     System.out.printf("%s%n", file.getFileName().toString());
 580                     return FileVisitResult.CONTINUE;
 581                 }
 582 
 583                 @Override
 584                 public FileVisitResult preVisitDirectory(Path dir,
 585                                                          BasicFileAttributes attrs)
 586                 {
 587                     indent();
 588                     System.out.printf("[%s]%n", dir.toString());
 589                     indent += 2;
 590                     return FileVisitResult.CONTINUE;
 591                 }
 592 
 593                 @Override
 594                 public FileVisitResult postVisitDirectory(Path dir,
 595                                                           IOException ioe)
 596                     throws IOException
 597                 {
 598                     indent -= 2;
 599                     return FileVisitResult.CONTINUE;
 600                 }
 601         });
 602     }
 603 
 604     private static void mkdirs(Path path) throws IOException {
 605         if (Files.exists(path))
 606             return;
 607         path = path.toAbsolutePath();
 608         Path parent = path.getParent();
 609         if (parent != null) {
 610             if (Files.notExists(parent))
 611                 mkdirs(parent);
 612         }
 613         Files.createDirectory(path);
 614     }
 615 
 616     private static void rmdirs(Path path) throws IOException {
 617         while (path != null && path.getNameCount() != 0) {
 618             Files.delete(path);
 619             path = path.getParent();
 620         }
 621     }
 622 
 623     // check the content of two paths are equal
 624     private static void checkEqual(Path src, Path dst) throws IOException
 625     {
 626         //System.out.printf("checking <%s> vs <%s>...%n",
 627         //                  src.toString(), dst.toString());
 628 
 629         //streams
 630         byte[] bufSrc = new byte[8192];
 631         byte[] bufDst = new byte[8192];
 632         try (InputStream isSrc = Files.newInputStream(src);
 633              InputStream isDst = Files.newInputStream(dst))
 634         {
 635             int nSrc = 0;
 636             while ((nSrc = isSrc.read(bufSrc)) != -1) {
 637                 int nDst = 0;
 638                 while (nDst < nSrc) {
 639                     int n = isDst.read(bufDst, nDst, nSrc - nDst);
 640                     if (n == -1) {
 641                         System.out.printf("checking <%s> vs <%s>...%n",
 642                                           src.toString(), dst.toString());
 643                         throw new RuntimeException("CHECK FAILED!");
 644                     }
 645                     nDst += n;
 646                 }
 647                 while (--nSrc >= 0) {
 648                     if (bufSrc[nSrc] != bufDst[nSrc]) {
 649                         System.out.printf("checking <%s> vs <%s>...%n",
 650                                           src.toString(), dst.toString());
 651                         throw new RuntimeException("CHECK FAILED!");
 652                     }
 653                     nSrc--;
 654                 }
 655             }
 656         }
 657 
 658         // channels
 659         try (SeekableByteChannel chSrc = Files.newByteChannel(src);
 660              SeekableByteChannel chDst = Files.newByteChannel(dst))
 661         {
 662             if (chSrc.size() != chDst.size()) {
 663                 System.out.printf("src[%s].size=%d, dst[%s].size=%d%n",
 664                                   chSrc.toString(), chSrc.size(),
 665                                   chDst.toString(), chDst.size());
 666                 throw new RuntimeException("CHECK FAILED!");
 667             }
 668             ByteBuffer bbSrc = ByteBuffer.allocate(8192);
 669             ByteBuffer bbDst = ByteBuffer.allocate(8192);
 670 
 671             int nSrc = 0;
 672             while ((nSrc = chSrc.read(bbSrc)) != -1) {
 673                 int nDst = chDst.read(bbDst);
 674                 if (nSrc != nDst) {
 675                     System.out.printf("checking <%s> vs <%s>...%n",
 676                                       src.toString(), dst.toString());
 677                     throw new RuntimeException("CHECK FAILED!");
 678                 }
 679                 while (--nSrc >= 0) {
 680                     if (bbSrc.get(nSrc) != bbDst.get(nSrc)) {
 681                         System.out.printf("checking <%s> vs <%s>...%n",
 682                                           src.toString(), dst.toString());
 683                         throw new RuntimeException("CHECK FAILED!");
 684                     }
 685                     nSrc--;
 686                 }
 687                 bbSrc.flip();
 688                 bbDst.flip();
 689             }
 690 
 691             // Check if source read position is at the end
 692             if (chSrc.position() != chSrc.size()) {
 693                 System.out.printf("src[%s]: size=%d, position=%d%n",
 694                                   chSrc.toString(), chSrc.size(), chSrc.position());
 695                 throw new RuntimeException("CHECK FAILED!");
 696             }
 697 
 698             // Check if destination read position is at the end
 699             if (chDst.position() != chDst.size()) {
 700                 System.out.printf("dst[%s]: size=%d, position=%d%n",
 701                                   chDst.toString(), chDst.size(), chDst.position());
 702                 throw new RuntimeException("CHECK FAILED!");
 703             }
 704         } catch (IOException x) {
 705             x.printStackTrace();
 706         }
 707     }
 708 
 709     private static void fchCopy(Path src, Path dst) throws IOException
 710     {
 711         Set<OpenOption> read = new HashSet<>();
 712         read.add(READ);
 713         Set<OpenOption> openwrite = new HashSet<>();
 714         openwrite.add(CREATE_NEW);
 715         openwrite.add(WRITE);
 716 
 717         try (FileChannel srcFc = src.getFileSystem()
 718                                     .provider()
 719                                     .newFileChannel(src, read);
 720              FileChannel dstFc = dst.getFileSystem()
 721                                     .provider()
 722                                     .newFileChannel(dst, openwrite))
 723         {
 724             ByteBuffer bb = ByteBuffer.allocate(8192);
 725             while (srcFc.read(bb) >= 0) {
 726                 bb.flip();
 727                 dstFc.write(bb);
 728                 bb.clear();
 729             }
 730         }
 731     }
 732 
 733     private static void chCopy(Path src, Path dst) throws IOException
 734     {
 735         Set<OpenOption> read = new HashSet<>();
 736         read.add(READ);
 737         Set<OpenOption> openwrite = new HashSet<>();
 738         openwrite.add(CREATE_NEW);
 739         openwrite.add(WRITE);
 740 
 741         try (SeekableByteChannel srcCh = Files.newByteChannel(src, read);
 742              SeekableByteChannel dstCh = Files.newByteChannel(dst, openwrite))
 743         {
 744 
 745             ByteBuffer bb = ByteBuffer.allocate(8192);
 746             while (srcCh.read(bb) >= 0) {
 747                 bb.flip();
 748                 dstCh.write(bb);
 749                 bb.clear();
 750             }
 751 
 752             // Check if source read position is at the end
 753             if (srcCh.position() != srcCh.size()) {
 754                 System.out.printf("src[%s]: size=%d, position=%d%n",
 755                                   srcCh.toString(), srcCh.size(), srcCh.position());
 756                 throw new RuntimeException("CHECK FAILED!");
 757             }
 758 
 759             // Check if destination write position is at the end
 760             if (dstCh.position() != dstCh.size()) {
 761                 System.out.printf("dst[%s]: size=%d, position=%d%n",
 762                                   dstCh.toString(), dstCh.size(), dstCh.position());
 763                 throw new RuntimeException("CHECK FAILED!");
 764             }
 765         }
 766     }
 767 
 768     private static void streamCopy(Path src, Path dst) throws IOException
 769     {
 770         byte[] buf = new byte[8192];
 771         try (InputStream isSrc = Files.newInputStream(src);
 772              OutputStream osDst = Files.newOutputStream(dst))
 773         {
 774             int n = 0;
 775             while ((n = isSrc.read(buf)) != -1) {
 776                 osDst.write(buf, 0, n);
 777             }
 778         }
 779     }
 780 
 781     static void channel(FileSystem fs, Path path)
 782         throws Exception
 783     {
 784         System.out.println("test ByteChannel...");
 785         Set<OpenOption> read = new HashSet<>();
 786         read.add(READ);
 787         int n = 0;
 788         ByteBuffer bb = null;
 789         ByteBuffer bb2 = null;
 790         int N = 120;
 791 
 792         try (SeekableByteChannel sbc = Files.newByteChannel(path)) {
 793             System.out.printf("   sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size());
 794             if (sbc.position() != 0) {
 795                 throw new RuntimeException("CHECK FAILED!");
 796             }
 797 
 798             bb = ByteBuffer.allocate((int)sbc.size());
 799             n = sbc.read(bb);
 800             System.out.printf("   sbc[1]: read=%d, pos=%d, size=%d%n",
 801                               n, sbc.position(), sbc.size());
 802             if (sbc.position() != sbc.size()) {
 803                 throw new RuntimeException("CHECK FAILED!");
 804             }
 805             bb2 = ByteBuffer.allocate((int)sbc.size());
 806         }
 807 
 808         // sbc.position(pos) is not supported in current version
 809         // try the FileChannel
 810         try (SeekableByteChannel sbc = fs.provider().newFileChannel(path, read)) {
 811             sbc.position(N);
 812             System.out.printf("   sbc[2]: pos=%d, size=%d%n",
 813                               sbc.position(), sbc.size());
 814             if (sbc.position() != N) {
 815                 throw new RuntimeException("CHECK FAILED!");
 816             }
 817             bb2.limit(100);
 818             n = sbc.read(bb2);
 819             System.out.printf("   sbc[3]: read=%d, pos=%d, size=%d%n",
 820                               n, sbc.position(), sbc.size());
 821             if (n < 0 || sbc.position() != (N + n)) {
 822                 throw new RuntimeException("CHECK FAILED!");
 823             }
 824             System.out.printf("   sbc[4]: bb[%d]=%d, bb1[0]=%d%n",
 825                               N, bb.get(N) & 0xff, bb2.get(0) & 0xff);
 826         }
 827     }
 828 
 829     // create parents if does not exist
 830     static Path getPathWithParents(FileSystem fs, String name)
 831         throws Exception
 832     {
 833         Path path = fs.getPath(name);
 834         Path parent = path.getParent();
 835         if (parent != null && Files.notExists(parent))
 836             mkdirs(parent);
 837         return path;
 838     }
 839 }