1 /*
   2  * Copyright (c) 2008, 2017, 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 /* @test
  25  * @bug 4313887 6838333 6917021 7006126 6950237 8006645
  26  * @summary Unit test for java.nio.file.Files copy and move methods (use -Dseed=X to set PRNG seed)
  27  * @library .. /test/lib
  28  * @build jdk.test.lib.RandomFactory
  29  *        CopyAndMove PassThroughFileSystem
  30  * @run main/othervm CopyAndMove
  31  * @key randomness
  32  */
  33 
  34 import java.nio.ByteBuffer;
  35 import java.nio.file.*;
  36 import static java.nio.file.Files.*;
  37 import static java.nio.file.StandardCopyOption.*;
  38 import static java.nio.file.LinkOption.*;
  39 import java.nio.file.attribute.*;
  40 import java.io.*;
  41 import java.util.*;
  42 import java.util.concurrent.TimeUnit;
  43 import jdk.test.lib.RandomFactory;
  44 
  45 public class CopyAndMove {
  46     static final Random rand = RandomFactory.getRandom();
  47     static boolean heads() { return rand.nextBoolean(); }
  48     private static boolean testPosixAttributes = false;
  49 
  50     public static void main(String[] args) throws Exception {
  51         Path dir1 = TestUtil.createTemporaryDirectory();
  52         try {
  53 
  54             // Same directory
  55             testPosixAttributes = getFileStore(dir1).supportsFileAttributeView("posix");
  56             testCopyFileToFile(dir1, dir1, TestUtil.supportsLinks(dir1));
  57             testMove(dir1, dir1, TestUtil.supportsLinks(dir1));
  58 
  59             // Different directories. Use test.dir if possible as it might be
  60             // a different volume/file system and so improve test coverage.
  61             String testDir = System.getProperty("test.dir", ".");
  62             Path dir2 = TestUtil.createTemporaryDirectory(testDir);
  63             try {
  64                 boolean testSymbolicLinks =
  65                     TestUtil.supportsLinks(dir1) && TestUtil.supportsLinks(dir2);
  66                 testPosixAttributes = getFileStore(dir1).supportsFileAttributeView("posix") &&
  67                                       getFileStore(dir2).supportsFileAttributeView("posix");
  68                 testCopyFileToFile(dir1, dir2, testSymbolicLinks);
  69                 testMove(dir1, dir2, testSymbolicLinks);
  70             } finally {
  71                 TestUtil.removeAll(dir2);
  72             }
  73 
  74             // Target is location associated with custom provider
  75             Path dir3 = PassThroughFileSystem.create().getPath(dir1.toString());
  76             testPosixAttributes = getFileStore(dir1).supportsFileAttributeView("posix") &&
  77                                   getFileStore(dir3).supportsFileAttributeView("posix");
  78             testCopyFileToFile(dir1, dir3, false);
  79             testMove(dir1, dir3, false);
  80 
  81             // Test copy(InputStream,Path) and copy(Path,OutputStream)
  82             testCopyInputStreamToFile();
  83             testCopyFileToOuputStream();
  84 
  85         } finally {
  86             TestUtil.removeAll(dir1);
  87         }
  88     }
  89 
  90     static void checkBasicAttributes(BasicFileAttributes attrs1,
  91                                      BasicFileAttributes attrs2)
  92     {
  93         // check file type
  94         assertTrue(attrs1.isRegularFile() == attrs2.isRegularFile());
  95         assertTrue(attrs1.isDirectory() == attrs2.isDirectory());
  96         assertTrue(attrs1.isSymbolicLink() == attrs2.isSymbolicLink());
  97         assertTrue(attrs1.isOther() == attrs2.isOther());
  98 
  99         // check last modified time if not a symbolic link
 100         if (!attrs1.isSymbolicLink()) {
 101             long time1 = attrs1.lastModifiedTime().to(TimeUnit.SECONDS);
 102             long time2 = attrs2.lastModifiedTime().to(TimeUnit.SECONDS);
 103 
 104             if (time1 != time2) {
 105                 System.err.format("File time for %s is %s\n", attrs1.fileKey(), attrs1.lastModifiedTime());
 106                 System.err.format("File time for %s is %s\n", attrs2.fileKey(), attrs2.lastModifiedTime());
 107                 assertTrue(false);
 108             }
 109         }
 110 
 111         // check size
 112         if (attrs1.isRegularFile())
 113             assertTrue(attrs1.size() == attrs2.size());
 114     }
 115 
 116     static void checkPosixAttributes(PosixFileAttributes attrs1,
 117                                      PosixFileAttributes attrs2)
 118     {
 119         assertTrue(attrs1.permissions().equals(attrs2.permissions()));
 120         assertTrue(attrs1.owner().equals(attrs2.owner()));
 121         assertTrue(attrs1.group().equals(attrs2.group()));
 122     }
 123 
 124     static void checkDosAttributes(DosFileAttributes attrs1,
 125                                    DosFileAttributes attrs2)
 126     {
 127         assertTrue(attrs1.isReadOnly() == attrs2.isReadOnly());
 128         assertTrue(attrs1.isHidden() == attrs2.isHidden());
 129         assertTrue(attrs1.isSystem() == attrs2.isSystem());
 130     }
 131 
 132     static void checkUserDefinedFileAttributes(Map<String,ByteBuffer> attrs1,
 133                                      Map<String,ByteBuffer> attrs2)
 134     {
 135         assert attrs1.size() == attrs2.size();
 136         for (String name: attrs1.keySet()) {
 137             ByteBuffer bb1 = attrs1.get(name);
 138             ByteBuffer bb2 = attrs2.get(name);
 139             assertTrue(bb2 != null);
 140             assertTrue(bb1.equals(bb2));
 141         }
 142     }
 143 
 144     static Map<String,ByteBuffer> readUserDefinedFileAttributes(Path file)
 145         throws IOException
 146     {
 147         UserDefinedFileAttributeView view =
 148             getFileAttributeView(file, UserDefinedFileAttributeView.class);
 149         Map<String,ByteBuffer> result = new HashMap<>();
 150         for (String name: view.list()) {
 151             int size = view.size(name);
 152             ByteBuffer bb = ByteBuffer.allocate(size);
 153             int n = view.read(name, bb);
 154             assertTrue(n == size);
 155             bb.flip();
 156             result.put(name, bb);
 157         }
 158         return result;
 159     }
 160 
 161     // move source to target with verification
 162     static void moveAndVerify(Path source, Path target, CopyOption... options)
 163         throws IOException
 164     {
 165         // read attributes before file is moved
 166         BasicFileAttributes basicAttributes = null;
 167         PosixFileAttributes posixAttributes = null;
 168         DosFileAttributes dosAttributes = null;
 169         Map<String,ByteBuffer> namedAttributes = null;
 170 
 171         // get file attributes of source file
 172         String os = System.getProperty("os.name");
 173         if (os.startsWith("Windows")) {
 174             dosAttributes = readAttributes(source, DosFileAttributes.class, NOFOLLOW_LINKS);
 175             basicAttributes = dosAttributes;
 176         } else {
 177             posixAttributes = readAttributes(source, PosixFileAttributes.class, NOFOLLOW_LINKS);
 178             basicAttributes = posixAttributes;
 179         }
 180         if (basicAttributes == null)
 181             basicAttributes = readAttributes(source, BasicFileAttributes.class, NOFOLLOW_LINKS);
 182 
 183         // hash file contents if regular file
 184         int hash = (basicAttributes.isRegularFile()) ? computeHash(source) : 0;
 185 
 186         // record link target if symbolic link
 187         Path linkTarget = null;
 188         if (basicAttributes.isSymbolicLink())
 189             linkTarget = readSymbolicLink(source);
 190 
 191         // read named attributes if available (and file is not a sym link)
 192         if (!basicAttributes.isSymbolicLink() &&
 193             getFileStore(source).supportsFileAttributeView("xattr"))
 194         {
 195             namedAttributes = readUserDefinedFileAttributes(source);
 196         }
 197 
 198         // move file
 199         Path result = move(source, target, options);
 200         assertTrue(result == target);
 201 
 202         // verify source does not exist
 203         assertTrue(notExists(source));
 204 
 205         // verify file contents
 206         if (basicAttributes.isRegularFile()) {
 207             if (computeHash(target) != hash)
 208                 throw new RuntimeException("Failed to verify move of regular file");
 209         }
 210 
 211         // verify link target
 212         if (basicAttributes.isSymbolicLink()) {
 213             if (!readSymbolicLink(target).equals(linkTarget))
 214                 throw new RuntimeException("Failed to verify move of symbolic link");
 215         }
 216 
 217         // verify basic attributes
 218         checkBasicAttributes(basicAttributes,
 219             readAttributes(target, BasicFileAttributes.class, NOFOLLOW_LINKS));
 220 
 221         // verify other attributes when same provider
 222         if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
 223 
 224             // verify POSIX attributes
 225             if (posixAttributes != null &&
 226                 !basicAttributes.isSymbolicLink() &&
 227                 testPosixAttributes)
 228             {
 229                 checkPosixAttributes(posixAttributes,
 230                     readAttributes(target, PosixFileAttributes.class, NOFOLLOW_LINKS));
 231             }
 232 
 233             // verify DOS attributes
 234             if (dosAttributes != null && !basicAttributes.isSymbolicLink()) {
 235                 DosFileAttributes attrs =
 236                     readAttributes(target, DosFileAttributes.class, NOFOLLOW_LINKS);
 237                 checkDosAttributes(dosAttributes, attrs);
 238             }
 239 
 240             // verify named attributes
 241             if (namedAttributes != null &&
 242                 getFileStore(target).supportsFileAttributeView("xattr"))
 243             {
 244                 checkUserDefinedFileAttributes(namedAttributes,
 245                                                readUserDefinedFileAttributes(target));
 246             }
 247         }
 248     }
 249 
 250     /**
 251      * Tests all possible ways to invoke move
 252      */
 253     static void testMove(Path dir1, Path dir2, boolean supportsLinks)
 254         throws IOException
 255     {
 256         Path source, target, entry;
 257 
 258         boolean sameDevice = getFileStore(dir1).equals(getFileStore(dir2));
 259 
 260         // -- regular file --
 261 
 262         /**
 263          * Test: move regular file, target does not exist
 264          */
 265         source = createSourceFile(dir1);
 266         target = getTargetFile(dir2);
 267         moveAndVerify(source, target);
 268         delete(target);
 269 
 270         /**
 271          * Test: move regular file, target exists
 272          */
 273         source = createSourceFile(dir1);
 274         target = getTargetFile(dir2);
 275         createFile(target);
 276         try {
 277             moveAndVerify(source, target);
 278             throw new RuntimeException("FileAlreadyExistsException expected");
 279         } catch (FileAlreadyExistsException x) {
 280         }
 281         delete(target);
 282         createDirectory(target);
 283         try {
 284             moveAndVerify(source, target);
 285             throw new RuntimeException("FileAlreadyExistsException expected");
 286         } catch (FileAlreadyExistsException x) {
 287         }
 288         delete(source);
 289         delete(target);
 290 
 291         /**
 292          * Test: move regular file, target does not exist
 293          */
 294         source = createSourceFile(dir1);
 295         target = getTargetFile(dir2);
 296         moveAndVerify(source, target, REPLACE_EXISTING);
 297         delete(target);
 298 
 299         /**
 300          * Test: move regular file, target exists
 301          */
 302         source = createSourceFile(dir1);
 303         target = getTargetFile(dir2);
 304         createFile(target);
 305         moveAndVerify(source, target, REPLACE_EXISTING);
 306         delete(target);
 307 
 308         /**
 309          * Test: move regular file, target exists and is empty directory
 310          */
 311         source = createSourceFile(dir1);
 312         target = getTargetFile(dir2);
 313         createDirectory(target);
 314         moveAndVerify(source, target, REPLACE_EXISTING);
 315         delete(target);
 316 
 317         /**
 318          * Test: move regular file, target exists and is non-empty directory
 319          */
 320         source = createSourceFile(dir1);
 321         target = getTargetFile(dir2);
 322         createDirectory(target);
 323         entry = target.resolve("foo");
 324         createFile(entry);
 325         try {
 326             moveAndVerify(source, target);
 327             throw new RuntimeException("FileAlreadyExistsException expected");
 328         } catch (FileAlreadyExistsException x) {
 329         }
 330         delete(entry);
 331         delete(source);
 332         delete(target);
 333 
 334         /**
 335          * Test atomic move of regular file (same file store)
 336          */
 337         source = createSourceFile(dir1);
 338         target = getTargetFile(dir1);
 339         moveAndVerify(source, target, ATOMIC_MOVE);
 340         delete(target);
 341 
 342         /**
 343          * Test atomic move of regular file (different file store)
 344          */
 345         if (!sameDevice) {
 346             source = createSourceFile(dir1);
 347             target = getTargetFile(dir2);
 348             try {
 349                 moveAndVerify(source, target, ATOMIC_MOVE);
 350                 throw new RuntimeException("AtomicMoveNotSupportedException expected");
 351             } catch (AtomicMoveNotSupportedException x) {
 352             }
 353             delete(source);
 354         }
 355 
 356         // -- directories --
 357 
 358         /*
 359          * Test: move empty directory, target does not exist
 360          */
 361         source = createSourceDirectory(dir1);
 362         target = getTargetFile(dir2);
 363         moveAndVerify(source, target);
 364         delete(target);
 365 
 366         /**
 367          * Test: move empty directory, target exists
 368          */
 369         source = createSourceDirectory(dir1);
 370         target = getTargetFile(dir2);
 371         createFile(target);
 372         try {
 373             moveAndVerify(source, target);
 374             throw new RuntimeException("FileAlreadyExistsException expected");
 375         } catch (FileAlreadyExistsException x) {
 376         }
 377         delete(target);
 378         createDirectory(target);
 379         try {
 380             moveAndVerify(source, target);
 381             throw new RuntimeException("FileAlreadyExistsException expected");
 382         } catch (FileAlreadyExistsException x) {
 383         }
 384         delete(source);
 385         delete(target);
 386 
 387         /**
 388          * Test: move empty directory, target does not exist
 389          */
 390         source = createSourceDirectory(dir1);
 391         target = getTargetFile(dir2);
 392         moveAndVerify(source, target, REPLACE_EXISTING);
 393         delete(target);
 394 
 395         /**
 396          * Test: move empty directory, target exists
 397          */
 398         source = createSourceDirectory(dir1);
 399         target = getTargetFile(dir2);
 400         createFile(target);
 401         moveAndVerify(source, target, REPLACE_EXISTING);
 402         delete(target);
 403 
 404         /**
 405          * Test: move empty, target exists and is empty directory
 406          */
 407         source = createSourceDirectory(dir1);
 408         target = getTargetFile(dir2);
 409         createDirectory(target);
 410         moveAndVerify(source, target, REPLACE_EXISTING);
 411         delete(target);
 412 
 413         /**
 414          * Test: move empty directory, target exists and is non-empty directory
 415          */
 416         source = createSourceDirectory(dir1);
 417         target = getTargetFile(dir2);
 418         createDirectory(target);
 419         entry = target.resolve("foo");
 420         createFile(entry);
 421         try {
 422             moveAndVerify(source, target, REPLACE_EXISTING);
 423             throw new RuntimeException("DirectoryNotEmptyException expected");
 424         } catch (DirectoryNotEmptyException x) {
 425         }
 426         delete(entry);
 427         delete(source);
 428         delete(target);
 429 
 430         /**
 431          * Test: move non-empty directory (same file system)
 432          */
 433         source = createSourceDirectory(dir1);
 434         createFile(source.resolve("foo"));
 435         target = getTargetFile(dir1);
 436         moveAndVerify(source, target);
 437         delete(target.resolve("foo"));
 438         delete(target);
 439 
 440         /**
 441          * Test: move non-empty directory (different file store)
 442          */
 443         if (!sameDevice) {
 444             source = createSourceDirectory(dir1);
 445             createFile(source.resolve("foo"));
 446             target = getTargetFile(dir2);
 447             try {
 448                 moveAndVerify(source, target);
 449                 throw new RuntimeException("IOException expected");
 450             } catch (IOException x) {
 451             }
 452             delete(source.resolve("foo"));
 453             delete(source);
 454         }
 455 
 456         /**
 457          * Test atomic move of directory (same file store)
 458          */
 459         source = createSourceDirectory(dir1);
 460         createFile(source.resolve("foo"));
 461         target = getTargetFile(dir1);
 462         moveAndVerify(source, target, ATOMIC_MOVE);
 463         delete(target.resolve("foo"));
 464         delete(target);
 465 
 466         // -- symbolic links --
 467 
 468         /**
 469          * Test: Move symbolic link to file, target does not exist
 470          */
 471         if (supportsLinks) {
 472             Path tmp = createSourceFile(dir1);
 473             source = dir1.resolve("link");
 474             createSymbolicLink(source, tmp);
 475             target = getTargetFile(dir2);
 476             moveAndVerify(source, target);
 477             delete(target);
 478             delete(tmp);
 479         }
 480 
 481         /**
 482          * Test: Move symbolic link to directory, target does not exist
 483          */
 484         if (supportsLinks) {
 485             source = dir1.resolve("link");
 486             createSymbolicLink(source, dir2);
 487             target = getTargetFile(dir2);
 488             moveAndVerify(source, target);
 489             delete(target);
 490         }
 491 
 492         /**
 493          * Test: Move broken symbolic link, target does not exists
 494          */
 495         if (supportsLinks) {
 496             Path tmp = Paths.get("doesnotexist");
 497             source = dir1.resolve("link");
 498             createSymbolicLink(source, tmp);
 499             target = getTargetFile(dir2);
 500             moveAndVerify(source, target);
 501             delete(target);
 502         }
 503 
 504         /**
 505          * Test: Move symbolic link, target exists
 506          */
 507         if (supportsLinks) {
 508             source = dir1.resolve("link");
 509             createSymbolicLink(source, dir2);
 510             target = getTargetFile(dir2);
 511             createFile(target);
 512             try {
 513                 moveAndVerify(source, target);
 514                 throw new RuntimeException("FileAlreadyExistsException expected");
 515             } catch (FileAlreadyExistsException x) {
 516             }
 517             delete(source);
 518             delete(target);
 519         }
 520 
 521         /**
 522          * Test: Move regular file, target exists
 523          */
 524         if (supportsLinks) {
 525             source = dir1.resolve("link");
 526             createSymbolicLink(source, dir2);
 527             target = getTargetFile(dir2);
 528             createFile(target);
 529             moveAndVerify(source, target, REPLACE_EXISTING);
 530             delete(target);
 531         }
 532 
 533         /**
 534          * Test: move symbolic link, target exists and is empty directory
 535          */
 536         if (supportsLinks) {
 537             source = dir1.resolve("link");
 538             createSymbolicLink(source, dir2);
 539             target = getTargetFile(dir2);
 540             createDirectory(target);
 541             moveAndVerify(source, target, REPLACE_EXISTING);
 542             delete(target);
 543         }
 544 
 545         /**
 546          * Test: symbolic link, target exists and is non-empty directory
 547          */
 548         if (supportsLinks) {
 549             source = dir1.resolve("link");
 550             createSymbolicLink(source, dir2);
 551             target = getTargetFile(dir2);
 552             createDirectory(target);
 553             entry = target.resolve("foo");
 554             createFile(entry);
 555             try {
 556                 moveAndVerify(source, target);
 557                 throw new RuntimeException("FileAlreadyExistsException expected");
 558             } catch (FileAlreadyExistsException x) {
 559             }
 560             delete(entry);
 561             delete(source);
 562             delete(target);
 563         }
 564 
 565         /**
 566          * Test atomic move of symbolic link (same file store)
 567          */
 568         if (supportsLinks) {
 569             source = dir1.resolve("link");
 570             createSymbolicLink(source, dir1);
 571             target = getTargetFile(dir2);
 572             createFile(target);
 573             moveAndVerify(source, target, REPLACE_EXISTING);
 574             delete(target);
 575         }
 576 
 577         // -- misc. tests --
 578 
 579         /**
 580          * Test nulls
 581          */
 582         source = createSourceFile(dir1);
 583         target = getTargetFile(dir2);
 584         try {
 585             move(null, target);
 586             throw new RuntimeException("NullPointerException expected");
 587         } catch (NullPointerException x) { }
 588         try {
 589             move(source, null);
 590             throw new RuntimeException("NullPointerException expected");
 591         } catch (NullPointerException x) { }
 592         try {
 593             move(source, target, (CopyOption[])null);
 594             throw new RuntimeException("NullPointerException expected");
 595         } catch (NullPointerException x) { }
 596         try {
 597             CopyOption[] opts = { REPLACE_EXISTING, null };
 598             move(source, target, opts);
 599             throw new RuntimeException("NullPointerException expected");
 600         } catch (NullPointerException x) { }
 601         delete(source);
 602 
 603         /**
 604          * Test UOE
 605          */
 606         source = createSourceFile(dir1);
 607         target = getTargetFile(dir2);
 608         try {
 609             move(source, target, new CopyOption() { });
 610         } catch (UnsupportedOperationException x) { }
 611         try {
 612             move(source, target, REPLACE_EXISTING,  new CopyOption() { });
 613         } catch (UnsupportedOperationException x) { }
 614         delete(source);
 615     }
 616 
 617     // copy source to target with verification
 618     static void copyAndVerify(Path source, Path target, CopyOption... options)
 619         throws IOException
 620     {
 621         Path result = copy(source, target, options);
 622         assertTrue(result == target);
 623 
 624         // get attributes of source and target file to verify copy
 625         boolean followLinks = true;
 626         LinkOption[] linkOptions = new LinkOption[0];
 627         boolean copyAttributes = false;
 628         for (CopyOption opt : options) {
 629             if (opt == NOFOLLOW_LINKS) {
 630                 followLinks = false;
 631                 linkOptions = new LinkOption[] { NOFOLLOW_LINKS };
 632             }
 633             if (opt == COPY_ATTRIBUTES)
 634                 copyAttributes = true;
 635         }
 636         BasicFileAttributes basicAttributes =
 637             readAttributes(source, BasicFileAttributes.class, linkOptions);
 638 
 639         // check hash if regular file
 640         if (basicAttributes.isRegularFile())
 641             assertTrue(computeHash(source) == computeHash(target));
 642 
 643         // check link target if symbolic link
 644         if (basicAttributes.isSymbolicLink())
 645             assert(readSymbolicLink(source).equals(readSymbolicLink(target)));
 646 
 647         // check that attributes are copied
 648         if (copyAttributes && followLinks) {
 649             checkBasicAttributes(basicAttributes,
 650                 readAttributes(source, BasicFileAttributes.class, linkOptions));
 651 
 652             // verify other attributes when same provider
 653             if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
 654 
 655                 // check POSIX attributes are copied
 656                 String os = System.getProperty("os.name");
 657                 if ((os.equals("SunOS") || os.equals("Linux")) &&
 658                     testPosixAttributes)
 659                 {
 660                     checkPosixAttributes(
 661                         readAttributes(source, PosixFileAttributes.class, linkOptions),
 662                         readAttributes(target, PosixFileAttributes.class, linkOptions));
 663                 }
 664 
 665                 // check DOS attributes are copied
 666                 if (os.startsWith("Windows")) {
 667                     checkDosAttributes(
 668                         readAttributes(source, DosFileAttributes.class, linkOptions),
 669                         readAttributes(target, DosFileAttributes.class, linkOptions));
 670                 }
 671 
 672                 // check named attributes are copied
 673                 if (followLinks &&
 674                     getFileStore(source).supportsFileAttributeView("xattr") &&
 675                     getFileStore(target).supportsFileAttributeView("xattr"))
 676                 {
 677                     checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source),
 678                                                    readUserDefinedFileAttributes(target));
 679                 }
 680             }
 681         }
 682     }
 683 
 684     /**
 685      * Tests all possible ways to invoke copy to copy a file to a file
 686      */
 687     static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsLinks)
 688         throws IOException
 689     {
 690         Path source, target, link, entry;
 691 
 692         // -- regular file --
 693 
 694         /**
 695          * Test: move regular file, target does not exist
 696          */
 697         source = createSourceFile(dir1);
 698         target = getTargetFile(dir2);
 699         copyAndVerify(source, target);
 700         delete(source);
 701         delete(target);
 702 
 703         /**
 704          * Test: copy regular file, target exists
 705          */
 706         source = createSourceFile(dir1);
 707         target = getTargetFile(dir2);
 708         createFile(target);
 709         try {
 710             copyAndVerify(source, target);
 711             throw new RuntimeException("FileAlreadyExistsException expected");
 712         } catch (FileAlreadyExistsException x) {
 713         }
 714         delete(target);
 715         createDirectory(target);
 716         try {
 717             copyAndVerify(source, target);
 718             throw new RuntimeException("FileAlreadyExistsException expected");
 719         } catch (FileAlreadyExistsException x) {
 720         }
 721         delete(source);
 722         delete(target);
 723 
 724         /**
 725          * Test: copy regular file, target does not exist
 726          */
 727         source = createSourceFile(dir1);
 728         target = getTargetFile(dir2);
 729         copyAndVerify(source, target, REPLACE_EXISTING);
 730         delete(source);
 731         delete(target);
 732 
 733         /**
 734          * Test: copy regular file, target exists
 735          */
 736         source = createSourceFile(dir1);
 737         target = getTargetFile(dir2);
 738         createFile(target);
 739         copyAndVerify(source, target, REPLACE_EXISTING);
 740         delete(source);
 741         delete(target);
 742 
 743         /**
 744          * Test: copy regular file, target exists and is empty directory
 745          */
 746         source = createSourceFile(dir1);
 747         target = getTargetFile(dir2);
 748         createDirectory(target);
 749         copyAndVerify(source, target, REPLACE_EXISTING);
 750         delete(source);
 751         delete(target);
 752 
 753         /**
 754          * Test: copy regular file, target exists and is non-empty directory
 755          */
 756         source = createSourceFile(dir1);
 757         target = getTargetFile(dir2);
 758         createDirectory(target);
 759         entry = target.resolve("foo");
 760         createFile(entry);
 761         try {
 762             copyAndVerify(source, target);
 763             throw new RuntimeException("FileAlreadyExistsException expected");
 764         } catch (FileAlreadyExistsException x) {
 765         }
 766         delete(entry);
 767         delete(source);
 768         delete(target);
 769 
 770         /**
 771          * Test: copy regular file + attributes
 772          */
 773         source = createSourceFile(dir1);
 774         target = getTargetFile(dir2);
 775         copyAndVerify(source, target, COPY_ATTRIBUTES);
 776         delete(source);
 777         delete(target);
 778 
 779 
 780         // -- directory --
 781 
 782         /*
 783          * Test: copy directory, target does not exist
 784          */
 785         source = createSourceDirectory(dir1);
 786         target = getTargetFile(dir2);
 787         copyAndVerify(source, target);
 788         delete(source);
 789         delete(target);
 790 
 791         /**
 792          * Test: copy directory, target exists
 793          */
 794         source = createSourceDirectory(dir1);
 795         target = getTargetFile(dir2);
 796         createFile(target);
 797         try {
 798             copyAndVerify(source, target);
 799             throw new RuntimeException("FileAlreadyExistsException expected");
 800         } catch (FileAlreadyExistsException x) {
 801         }
 802         delete(target);
 803         createDirectory(target);
 804         try {
 805             copyAndVerify(source, target);
 806             throw new RuntimeException("FileAlreadyExistsException expected");
 807         } catch (FileAlreadyExistsException x) {
 808         }
 809         delete(source);
 810         delete(target);
 811 
 812         /**
 813          * Test: copy directory, target does not exist
 814          */
 815         source = createSourceDirectory(dir1);
 816         target = getTargetFile(dir2);
 817         copyAndVerify(source, target, REPLACE_EXISTING);
 818         delete(source);
 819         delete(target);
 820 
 821         /**
 822          * Test: copy directory, target exists
 823          */
 824         source = createSourceDirectory(dir1);
 825         target = getTargetFile(dir2);
 826         createFile(target);
 827         copyAndVerify(source, target, REPLACE_EXISTING);
 828         delete(source);
 829         delete(target);
 830 
 831         /**
 832          * Test: copy directory, target exists and is empty directory
 833          */
 834         source = createSourceDirectory(dir1);
 835         target = getTargetFile(dir2);
 836         createDirectory(target);
 837         copyAndVerify(source, target, REPLACE_EXISTING);
 838         delete(source);
 839         delete(target);
 840 
 841         /**
 842          * Test: copy directory, target exists and is non-empty directory
 843          */
 844         source = createSourceDirectory(dir1);
 845         target = getTargetFile(dir2);
 846         createDirectory(target);
 847         entry = target.resolve("foo");
 848         createFile(entry);
 849         try {
 850             copyAndVerify(source, target, REPLACE_EXISTING);
 851             throw new RuntimeException("DirectoryNotEmptyException expected");
 852         } catch (DirectoryNotEmptyException x) {
 853         }
 854         delete(entry);
 855         delete(source);
 856         delete(target);
 857 
 858         /*
 859          * Test: copy directory + attributes
 860          */
 861         source = createSourceDirectory(dir1);
 862         target = getTargetFile(dir2);
 863         copyAndVerify(source, target, COPY_ATTRIBUTES);
 864         delete(source);
 865         delete(target);
 866 
 867         // -- symbolic links --
 868 
 869         /**
 870          * Test: Follow link
 871          */
 872         if (supportsLinks) {
 873             source = createSourceFile(dir1);
 874             link = dir1.resolve("link");
 875             createSymbolicLink(link, source);
 876             target = getTargetFile(dir2);
 877             copyAndVerify(link, target);
 878             delete(link);
 879             delete(source);
 880         }
 881 
 882         /**
 883          * Test: Copy link (to file)
 884          */
 885         if (supportsLinks) {
 886             source = createSourceFile(dir1);
 887             link = dir1.resolve("link");
 888             createSymbolicLink(link, source);
 889             target = getTargetFile(dir2);
 890             copyAndVerify(link, target, NOFOLLOW_LINKS);
 891             delete(link);
 892             delete(source);
 893         }
 894 
 895         /**
 896          * Test: Copy link (to directory)
 897          */
 898         if (supportsLinks) {
 899             source = dir1.resolve("mydir");
 900             createDirectory(source);
 901             link = dir1.resolve("link");
 902             createSymbolicLink(link, source);
 903             target = getTargetFile(dir2);
 904             copyAndVerify(link, target, NOFOLLOW_LINKS);
 905             delete(link);
 906             delete(source);
 907         }
 908 
 909         /**
 910          * Test: Copy broken link
 911          */
 912         if (supportsLinks) {
 913             assertTrue(notExists(source));
 914             link = dir1.resolve("link");
 915             createSymbolicLink(link, source);
 916             target = getTargetFile(dir2);
 917             copyAndVerify(link, target, NOFOLLOW_LINKS);
 918             delete(link);
 919         }
 920 
 921         /**
 922          * Test: Copy link to UNC (Windows only)
 923          */
 924         if (supportsLinks &&
 925             System.getProperty("os.name").startsWith("Windows"))
 926         {
 927             Path unc = Paths.get("\\\\rialto\\share\\file");
 928             link = dir1.resolve("link");
 929             createSymbolicLink(link, unc);
 930             target = getTargetFile(dir2);
 931             copyAndVerify(link, target, NOFOLLOW_LINKS);
 932             delete(link);
 933         }
 934 
 935         // -- misc. tests --
 936 
 937         /**
 938          * Test nulls
 939          */
 940         source = createSourceFile(dir1);
 941         target = getTargetFile(dir2);
 942         try {
 943             copy(source, null);
 944             throw new RuntimeException("NullPointerException expected");
 945         } catch (NullPointerException x) { }
 946         try {
 947             copy(source, target, (CopyOption[])null);
 948             throw new RuntimeException("NullPointerException expected");
 949         } catch (NullPointerException x) { }
 950         try {
 951             CopyOption[] opts = { REPLACE_EXISTING, null };
 952             copy(source, target, opts);
 953             throw new RuntimeException("NullPointerException expected");
 954         } catch (NullPointerException x) { }
 955         delete(source);
 956 
 957         /**
 958          * Test UOE
 959          */
 960         source = createSourceFile(dir1);
 961         target = getTargetFile(dir2);
 962         try {
 963             copy(source, target, new CopyOption() { });
 964         } catch (UnsupportedOperationException x) { }
 965         try {
 966             copy(source, target, REPLACE_EXISTING,  new CopyOption() { });
 967         } catch (UnsupportedOperationException x) { }
 968         delete(source);
 969     }
 970 
 971     /**
 972      * Test copy from an input stream to a file
 973      */
 974     static void testCopyInputStreamToFile() throws IOException {
 975         testCopyInputStreamToFile(0);
 976         for (int i=0; i<100; i++) {
 977             testCopyInputStreamToFile(rand.nextInt(32000));
 978         }
 979 
 980         // FileAlreadyExistsException
 981         Path target = createTempFile("blah", null);
 982         try {
 983             InputStream in = new ByteArrayInputStream(new byte[0]);
 984             try {
 985                 copy(in, target);
 986                 throw new RuntimeException("FileAlreadyExistsException expected");
 987             } catch (FileAlreadyExistsException ignore) { }
 988         } finally {
 989             delete(target);
 990         }
 991         Path tmpdir = createTempDirectory("blah");
 992         try {
 993             if (TestUtil.supportsLinks(tmpdir)) {
 994                 Path link = createSymbolicLink(tmpdir.resolve("link"),
 995                                                   tmpdir.resolve("target"));
 996                 try {
 997                     InputStream in = new ByteArrayInputStream(new byte[0]);
 998                     try {
 999                         copy(in, link);
1000                         throw new RuntimeException("FileAlreadyExistsException expected");
1001                     } catch (FileAlreadyExistsException ignore) { }
1002                 } finally {
1003                     delete(link);
1004                 }
1005             }
1006         } finally {
1007             delete(tmpdir);
1008         }
1009 
1010 
1011         // nulls
1012         try {
1013             copy((InputStream)null, target);
1014             throw new RuntimeException("NullPointerException expected");
1015         } catch (NullPointerException ignore) { }
1016         try {
1017             copy(new ByteArrayInputStream(new byte[0]), (Path)null);
1018             throw new RuntimeException("NullPointerException expected");
1019         } catch (NullPointerException ignore) { }
1020     }
1021 
1022     static void testCopyInputStreamToFile(int size) throws IOException {
1023         Path tmpdir = createTempDirectory("blah");
1024         Path source = tmpdir.resolve("source");
1025         Path target = tmpdir.resolve("target");
1026         try {
1027             boolean testReplaceExisting = rand.nextBoolean();
1028 
1029             // create source file
1030             byte[] b = new byte[size];
1031             rand.nextBytes(b);
1032             write(source, b);
1033 
1034             // target file might already exist
1035             if (testReplaceExisting && rand.nextBoolean()) {
1036                 write(target, new byte[rand.nextInt(512)]);
1037             }
1038 
1039             // copy from stream to file
1040             InputStream in = new FileInputStream(source.toFile());
1041             try {
1042                 long n;
1043                 if (testReplaceExisting) {
1044                     n = copy(in, target, StandardCopyOption.REPLACE_EXISTING);
1045                 } else {
1046                     n = copy(in, target);
1047                 }
1048                 assertTrue(in.read() == -1);   // EOF
1049                 assertTrue(n == size);
1050                 assertTrue(size(target) == size);
1051             } finally {
1052                 in.close();
1053             }
1054 
1055             // check file
1056             byte[] read = readAllBytes(target);
1057             assertTrue(Arrays.equals(read, b));
1058 
1059         } finally {
1060             deleteIfExists(source);
1061             deleteIfExists(target);
1062             delete(tmpdir);
1063         }
1064     }
1065 
1066     /**
1067      * Test copy from file to output stream
1068      */
1069     static void testCopyFileToOuputStream() throws IOException {
1070         testCopyFileToOuputStream(0);
1071         for (int i=0; i<100; i++) {
1072             testCopyFileToOuputStream(rand.nextInt(32000));
1073         }
1074 
1075         // nulls
1076         try {
1077             copy((Path)null, new ByteArrayOutputStream());
1078             throw new RuntimeException("NullPointerException expected");
1079         } catch (NullPointerException ignore) { }
1080         try {
1081             Path source = createTempFile("blah", null);
1082             delete(source);
1083             copy(source, (OutputStream)null);
1084             throw new RuntimeException("NullPointerException expected");
1085         } catch (NullPointerException ignore) { }
1086     }
1087 
1088     static void testCopyFileToOuputStream(int size) throws IOException {
1089         Path source = createTempFile("blah", null);
1090         try {
1091             byte[] b = new byte[size];
1092             rand.nextBytes(b);
1093             write(source, b);
1094 
1095             ByteArrayOutputStream out = new ByteArrayOutputStream();
1096 
1097             long n = copy(source, out);
1098             assertTrue(n == size);
1099             assertTrue(out.size() == size);
1100 
1101             byte[] read = out.toByteArray();
1102             assertTrue(Arrays.equals(read, b));
1103 
1104             // check output stream is open
1105             out.write(0);
1106             assertTrue(out.size() == size+1);
1107         } finally {
1108             delete(source);
1109         }
1110     }
1111 
1112     static void assertTrue(boolean value) {
1113         if (!value)
1114             throw new RuntimeException("Assertion failed");
1115     }
1116 
1117     // computes simple hash of the given file
1118     static int computeHash(Path file) throws IOException {
1119         int h = 0;
1120 
1121         try (InputStream in = newInputStream(file)) {
1122             byte[] buf = new byte[1024];
1123             int n;
1124             do {
1125                 n = in.read(buf);
1126                 for (int i=0; i<n; i++) {
1127                     h = 31*h + (buf[i] & 0xff);
1128                 }
1129             } while (n > 0);
1130         }
1131         return h;
1132     }
1133 
1134     // create file of random size in given directory
1135     static Path createSourceFile(Path dir) throws IOException {
1136         String name = "source" + Integer.toString(rand.nextInt());
1137         Path file = dir.resolve(name);
1138         createFile(file);
1139         byte[] bytes = new byte[rand.nextInt(128*1024)];
1140         rand.nextBytes(bytes);
1141         try (OutputStream out = newOutputStream(file)) {
1142             out.write(bytes);
1143         }
1144         randomizeAttributes(file);
1145         return file;
1146     }
1147 
1148     // create directory in the given directory
1149     static Path createSourceDirectory(Path dir) throws IOException {
1150         String name = "sourcedir" + Integer.toString(rand.nextInt());
1151         Path subdir = dir.resolve(name);
1152         createDirectory(subdir);
1153         randomizeAttributes(subdir);
1154         return subdir;
1155     }
1156 
1157     // "randomize" the file attributes of the given file.
1158     static void randomizeAttributes(Path file) throws IOException {
1159         String os = System.getProperty("os.name");
1160         boolean isWindows = os.startsWith("Windows");
1161         boolean isUnix = os.equals("SunOS") || os.equals("Linux");
1162         boolean isDirectory = isDirectory(file, NOFOLLOW_LINKS);
1163 
1164         if (isUnix) {
1165             Set<PosixFilePermission> perms =
1166                 getPosixFilePermissions(file, NOFOLLOW_LINKS);
1167             PosixFilePermission[] toChange = {
1168                 PosixFilePermission.GROUP_READ,
1169                 PosixFilePermission.GROUP_WRITE,
1170                 PosixFilePermission.GROUP_EXECUTE,
1171                 PosixFilePermission.OTHERS_READ,
1172                 PosixFilePermission.OTHERS_WRITE,
1173                 PosixFilePermission.OTHERS_EXECUTE
1174             };
1175             for (PosixFilePermission perm: toChange) {
1176                 if (heads()) {
1177                     perms.add(perm);
1178                 } else {
1179                     perms.remove(perm);
1180                 }
1181             }
1182             setPosixFilePermissions(file, perms);
1183         }
1184 
1185         if (isWindows) {
1186             DosFileAttributeView view =
1187                 getFileAttributeView(file, DosFileAttributeView.class, NOFOLLOW_LINKS);
1188             // only set or unset the hidden attribute
1189             view.setHidden(heads());
1190         }
1191 
1192         boolean addUserDefinedFileAttributes = heads() &&
1193             getFileStore(file).supportsFileAttributeView("xattr");
1194 
1195         // remove this when copying a direcory copies its named streams
1196         if (isWindows && isDirectory) addUserDefinedFileAttributes = false;
1197 
1198         if (addUserDefinedFileAttributes) {
1199             UserDefinedFileAttributeView view =
1200                 getFileAttributeView(file, UserDefinedFileAttributeView.class);
1201             int n = rand.nextInt(16);
1202             while (n > 0) {
1203                 byte[] value = new byte[1 + rand.nextInt(100)];
1204                 view.write("user." + Integer.toString(n), ByteBuffer.wrap(value));
1205                 n--;
1206             }
1207         }
1208     }
1209 
1210     // create name for file in given directory
1211     static Path getTargetFile(Path dir) throws IOException {
1212         String name = "target" + Integer.toString(rand.nextInt());
1213         return dir.resolve(name);
1214     }
1215  }