1 /*
   2  * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  * @bug 4313887 6838333 6917021 7006126 6950237 8006645 8201407
  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                 if (!(x instanceof DirectoryNotEmptyException)) {
 452                     throw new RuntimeException
 453                         ("DirectoryNotEmptyException expected", x);
 454                 }
 455             }
 456             delete(source.resolve("foo"));
 457             delete(source);
 458         }
 459 
 460         /**
 461          * Test atomic move of directory (same file store)
 462          */
 463         source = createSourceDirectory(dir1);
 464         createFile(source.resolve("foo"));
 465         target = getTargetFile(dir1);
 466         moveAndVerify(source, target, ATOMIC_MOVE);
 467         delete(target.resolve("foo"));
 468         delete(target);
 469 
 470         // -- symbolic links --
 471 
 472         /**
 473          * Test: Move symbolic link to file, target does not exist
 474          */
 475         if (supportsLinks) {
 476             Path tmp = createSourceFile(dir1);
 477             source = dir1.resolve("link");
 478             createSymbolicLink(source, tmp);
 479             target = getTargetFile(dir2);
 480             moveAndVerify(source, target);
 481             delete(target);
 482             delete(tmp);
 483         }
 484 
 485         /**
 486          * Test: Move symbolic link to directory, target does not exist
 487          */
 488         if (supportsLinks) {
 489             source = dir1.resolve("link");
 490             createSymbolicLink(source, dir2);
 491             target = getTargetFile(dir2);
 492             moveAndVerify(source, target);
 493             delete(target);
 494         }
 495 
 496         /**
 497          * Test: Move broken symbolic link, target does not exists
 498          */
 499         if (supportsLinks) {
 500             Path tmp = Paths.get("doesnotexist");
 501             source = dir1.resolve("link");
 502             createSymbolicLink(source, tmp);
 503             target = getTargetFile(dir2);
 504             moveAndVerify(source, target);
 505             delete(target);
 506         }
 507 
 508         /**
 509          * Test: Move symbolic link, target exists
 510          */
 511         if (supportsLinks) {
 512             source = dir1.resolve("link");
 513             createSymbolicLink(source, dir2);
 514             target = getTargetFile(dir2);
 515             createFile(target);
 516             try {
 517                 moveAndVerify(source, target);
 518                 throw new RuntimeException("FileAlreadyExistsException expected");
 519             } catch (FileAlreadyExistsException x) {
 520             }
 521             delete(source);
 522             delete(target);
 523         }
 524 
 525         /**
 526          * Test: Move regular file, target exists
 527          */
 528         if (supportsLinks) {
 529             source = dir1.resolve("link");
 530             createSymbolicLink(source, dir2);
 531             target = getTargetFile(dir2);
 532             createFile(target);
 533             moveAndVerify(source, target, REPLACE_EXISTING);
 534             delete(target);
 535         }
 536 
 537         /**
 538          * Test: move symbolic link, target exists and is empty directory
 539          */
 540         if (supportsLinks) {
 541             source = dir1.resolve("link");
 542             createSymbolicLink(source, dir2);
 543             target = getTargetFile(dir2);
 544             createDirectory(target);
 545             moveAndVerify(source, target, REPLACE_EXISTING);
 546             delete(target);
 547         }
 548 
 549         /**
 550          * Test: symbolic link, target exists and is non-empty directory
 551          */
 552         if (supportsLinks) {
 553             source = dir1.resolve("link");
 554             createSymbolicLink(source, dir2);
 555             target = getTargetFile(dir2);
 556             createDirectory(target);
 557             entry = target.resolve("foo");
 558             createFile(entry);
 559             try {
 560                 moveAndVerify(source, target);
 561                 throw new RuntimeException("FileAlreadyExistsException expected");
 562             } catch (FileAlreadyExistsException x) {
 563             }
 564             delete(entry);
 565             delete(source);
 566             delete(target);
 567         }
 568 
 569         /**
 570          * Test atomic move of symbolic link (same file store)
 571          */
 572         if (supportsLinks) {
 573             source = dir1.resolve("link");
 574             createSymbolicLink(source, dir1);
 575             target = getTargetFile(dir2);
 576             createFile(target);
 577             moveAndVerify(source, target, REPLACE_EXISTING);
 578             delete(target);
 579         }
 580 
 581         // -- misc. tests --
 582 
 583         /**
 584          * Test nulls
 585          */
 586         source = createSourceFile(dir1);
 587         target = getTargetFile(dir2);
 588         try {
 589             move(null, target);
 590             throw new RuntimeException("NullPointerException expected");
 591         } catch (NullPointerException x) { }
 592         try {
 593             move(source, null);
 594             throw new RuntimeException("NullPointerException expected");
 595         } catch (NullPointerException x) { }
 596         try {
 597             move(source, target, (CopyOption[])null);
 598             throw new RuntimeException("NullPointerException expected");
 599         } catch (NullPointerException x) { }
 600         try {
 601             CopyOption[] opts = { REPLACE_EXISTING, null };
 602             move(source, target, opts);
 603             throw new RuntimeException("NullPointerException expected");
 604         } catch (NullPointerException x) { }
 605         delete(source);
 606 
 607         /**
 608          * Test UOE
 609          */
 610         source = createSourceFile(dir1);
 611         target = getTargetFile(dir2);
 612         try {
 613             move(source, target, new CopyOption() { });
 614         } catch (UnsupportedOperationException x) { }
 615         try {
 616             move(source, target, REPLACE_EXISTING,  new CopyOption() { });
 617         } catch (UnsupportedOperationException x) { }
 618         delete(source);
 619     }
 620 
 621     // copy source to target with verification
 622     static void copyAndVerify(Path source, Path target, CopyOption... options)
 623         throws IOException
 624     {
 625         Path result = copy(source, target, options);
 626         assertTrue(result == target);
 627 
 628         // get attributes of source and target file to verify copy
 629         boolean followLinks = true;
 630         LinkOption[] linkOptions = new LinkOption[0];
 631         boolean copyAttributes = false;
 632         for (CopyOption opt : options) {
 633             if (opt == NOFOLLOW_LINKS) {
 634                 followLinks = false;
 635                 linkOptions = new LinkOption[] { NOFOLLOW_LINKS };
 636             }
 637             if (opt == COPY_ATTRIBUTES)
 638                 copyAttributes = true;
 639         }
 640         BasicFileAttributes basicAttributes =
 641             readAttributes(source, BasicFileAttributes.class, linkOptions);
 642 
 643         // check hash if regular file
 644         if (basicAttributes.isRegularFile())
 645             assertTrue(computeHash(source) == computeHash(target));
 646 
 647         // check link target if symbolic link
 648         if (basicAttributes.isSymbolicLink())
 649             assert(readSymbolicLink(source).equals(readSymbolicLink(target)));
 650 
 651         // check that attributes are copied
 652         if (copyAttributes && followLinks) {
 653             checkBasicAttributes(basicAttributes,
 654                 readAttributes(source, BasicFileAttributes.class, linkOptions));
 655 
 656             // verify other attributes when same provider
 657             if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
 658 
 659                 // check POSIX attributes are copied
 660                 String os = System.getProperty("os.name");
 661                 if ((os.equals("SunOS") || os.equals("Linux")) &&
 662                     testPosixAttributes)
 663                 {
 664                     checkPosixAttributes(
 665                         readAttributes(source, PosixFileAttributes.class, linkOptions),
 666                         readAttributes(target, PosixFileAttributes.class, linkOptions));
 667                 }
 668 
 669                 // check DOS attributes are copied
 670                 if (os.startsWith("Windows")) {
 671                     checkDosAttributes(
 672                         readAttributes(source, DosFileAttributes.class, linkOptions),
 673                         readAttributes(target, DosFileAttributes.class, linkOptions));
 674                 }
 675 
 676                 // check named attributes are copied
 677                 if (followLinks &&
 678                     getFileStore(source).supportsFileAttributeView("xattr") &&
 679                     getFileStore(target).supportsFileAttributeView("xattr"))
 680                 {
 681                     checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source),
 682                                                    readUserDefinedFileAttributes(target));
 683                 }
 684             }
 685         }
 686     }
 687 
 688     /**
 689      * Tests all possible ways to invoke copy to copy a file to a file
 690      */
 691     static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsLinks)
 692         throws IOException
 693     {
 694         Path source, target, link, entry;
 695 
 696         // -- regular file --
 697 
 698         /**
 699          * Test: move regular file, target does not exist
 700          */
 701         source = createSourceFile(dir1);
 702         target = getTargetFile(dir2);
 703         copyAndVerify(source, target);
 704         delete(source);
 705         delete(target);
 706 
 707         /**
 708          * Test: copy regular file, target exists
 709          */
 710         source = createSourceFile(dir1);
 711         target = getTargetFile(dir2);
 712         createFile(target);
 713         try {
 714             copyAndVerify(source, target);
 715             throw new RuntimeException("FileAlreadyExistsException expected");
 716         } catch (FileAlreadyExistsException x) {
 717         }
 718         delete(target);
 719         createDirectory(target);
 720         try {
 721             copyAndVerify(source, target);
 722             throw new RuntimeException("FileAlreadyExistsException expected");
 723         } catch (FileAlreadyExistsException x) {
 724         }
 725         delete(source);
 726         delete(target);
 727 
 728         /**
 729          * Test: copy regular file, target does not exist
 730          */
 731         source = createSourceFile(dir1);
 732         target = getTargetFile(dir2);
 733         copyAndVerify(source, target, REPLACE_EXISTING);
 734         delete(source);
 735         delete(target);
 736 
 737         /**
 738          * Test: copy regular file, target exists
 739          */
 740         source = createSourceFile(dir1);
 741         target = getTargetFile(dir2);
 742         createFile(target);
 743         copyAndVerify(source, target, REPLACE_EXISTING);
 744         delete(source);
 745         delete(target);
 746 
 747         /**
 748          * Test: copy regular file, target exists and is empty directory
 749          */
 750         source = createSourceFile(dir1);
 751         target = getTargetFile(dir2);
 752         createDirectory(target);
 753         copyAndVerify(source, target, REPLACE_EXISTING);
 754         delete(source);
 755         delete(target);
 756 
 757         /**
 758          * Test: copy regular file, target exists and is non-empty directory
 759          */
 760         source = createSourceFile(dir1);
 761         target = getTargetFile(dir2);
 762         createDirectory(target);
 763         entry = target.resolve("foo");
 764         createFile(entry);
 765         try {
 766             copyAndVerify(source, target);
 767             throw new RuntimeException("FileAlreadyExistsException expected");
 768         } catch (FileAlreadyExistsException x) {
 769         }
 770         delete(entry);
 771         delete(source);
 772         delete(target);
 773 
 774         /**
 775          * Test: copy regular file + attributes
 776          */
 777         source = createSourceFile(dir1);
 778         target = getTargetFile(dir2);
 779         copyAndVerify(source, target, COPY_ATTRIBUTES);
 780         delete(source);
 781         delete(target);
 782 
 783 
 784         // -- directory --
 785 
 786         /*
 787          * Test: copy directory, target does not exist
 788          */
 789         source = createSourceDirectory(dir1);
 790         target = getTargetFile(dir2);
 791         copyAndVerify(source, target);
 792         delete(source);
 793         delete(target);
 794 
 795         /**
 796          * Test: copy directory, target exists
 797          */
 798         source = createSourceDirectory(dir1);
 799         target = getTargetFile(dir2);
 800         createFile(target);
 801         try {
 802             copyAndVerify(source, target);
 803             throw new RuntimeException("FileAlreadyExistsException expected");
 804         } catch (FileAlreadyExistsException x) {
 805         }
 806         delete(target);
 807         createDirectory(target);
 808         try {
 809             copyAndVerify(source, target);
 810             throw new RuntimeException("FileAlreadyExistsException expected");
 811         } catch (FileAlreadyExistsException x) {
 812         }
 813         delete(source);
 814         delete(target);
 815 
 816         /**
 817          * Test: copy directory, target does not exist
 818          */
 819         source = createSourceDirectory(dir1);
 820         target = getTargetFile(dir2);
 821         copyAndVerify(source, target, REPLACE_EXISTING);
 822         delete(source);
 823         delete(target);
 824 
 825         /**
 826          * Test: copy directory, target exists
 827          */
 828         source = createSourceDirectory(dir1);
 829         target = getTargetFile(dir2);
 830         createFile(target);
 831         copyAndVerify(source, target, REPLACE_EXISTING);
 832         delete(source);
 833         delete(target);
 834 
 835         /**
 836          * Test: copy directory, target exists and is empty directory
 837          */
 838         source = createSourceDirectory(dir1);
 839         target = getTargetFile(dir2);
 840         createDirectory(target);
 841         copyAndVerify(source, target, REPLACE_EXISTING);
 842         delete(source);
 843         delete(target);
 844 
 845         /**
 846          * Test: copy directory, target exists and is non-empty directory
 847          */
 848         source = createSourceDirectory(dir1);
 849         target = getTargetFile(dir2);
 850         createDirectory(target);
 851         entry = target.resolve("foo");
 852         createFile(entry);
 853         try {
 854             copyAndVerify(source, target, REPLACE_EXISTING);
 855             throw new RuntimeException("DirectoryNotEmptyException expected");
 856         } catch (DirectoryNotEmptyException x) {
 857         }
 858         delete(entry);
 859         delete(source);
 860         delete(target);
 861 
 862         /*
 863          * Test: copy directory + attributes
 864          */
 865         source = createSourceDirectory(dir1);
 866         target = getTargetFile(dir2);
 867         copyAndVerify(source, target, COPY_ATTRIBUTES);
 868         delete(source);
 869         delete(target);
 870 
 871         // -- symbolic links --
 872 
 873         /**
 874          * Test: Follow link
 875          */
 876         if (supportsLinks) {
 877             source = createSourceFile(dir1);
 878             link = dir1.resolve("link");
 879             createSymbolicLink(link, source);
 880             target = getTargetFile(dir2);
 881             copyAndVerify(link, target);
 882             delete(link);
 883             delete(source);
 884         }
 885 
 886         /**
 887          * Test: Copy link (to file)
 888          */
 889         if (supportsLinks) {
 890             source = createSourceFile(dir1);
 891             link = dir1.resolve("link");
 892             createSymbolicLink(link, source);
 893             target = getTargetFile(dir2);
 894             copyAndVerify(link, target, NOFOLLOW_LINKS);
 895             delete(link);
 896             delete(source);
 897         }
 898 
 899         /**
 900          * Test: Copy link (to directory)
 901          */
 902         if (supportsLinks) {
 903             source = dir1.resolve("mydir");
 904             createDirectory(source);
 905             link = dir1.resolve("link");
 906             createSymbolicLink(link, source);
 907             target = getTargetFile(dir2);
 908             copyAndVerify(link, target, NOFOLLOW_LINKS);
 909             delete(link);
 910             delete(source);
 911         }
 912 
 913         /**
 914          * Test: Copy broken link
 915          */
 916         if (supportsLinks) {
 917             assertTrue(notExists(source));
 918             link = dir1.resolve("link");
 919             createSymbolicLink(link, source);
 920             target = getTargetFile(dir2);
 921             copyAndVerify(link, target, NOFOLLOW_LINKS);
 922             delete(link);
 923         }
 924 
 925         /**
 926          * Test: Copy link to UNC (Windows only)
 927          */
 928         if (supportsLinks &&
 929             System.getProperty("os.name").startsWith("Windows"))
 930         {
 931             Path unc = Paths.get("\\\\rialto\\share\\file");
 932             link = dir1.resolve("link");
 933             createSymbolicLink(link, unc);
 934             target = getTargetFile(dir2);
 935             copyAndVerify(link, target, NOFOLLOW_LINKS);
 936             delete(link);
 937         }
 938 
 939         // -- misc. tests --
 940 
 941         /**
 942          * Test nulls
 943          */
 944         source = createSourceFile(dir1);
 945         target = getTargetFile(dir2);
 946         try {
 947             copy(source, null);
 948             throw new RuntimeException("NullPointerException expected");
 949         } catch (NullPointerException x) { }
 950         try {
 951             copy(source, target, (CopyOption[])null);
 952             throw new RuntimeException("NullPointerException expected");
 953         } catch (NullPointerException x) { }
 954         try {
 955             CopyOption[] opts = { REPLACE_EXISTING, null };
 956             copy(source, target, opts);
 957             throw new RuntimeException("NullPointerException expected");
 958         } catch (NullPointerException x) { }
 959         delete(source);
 960 
 961         /**
 962          * Test UOE
 963          */
 964         source = createSourceFile(dir1);
 965         target = getTargetFile(dir2);
 966         try {
 967             copy(source, target, new CopyOption() { });
 968         } catch (UnsupportedOperationException x) { }
 969         try {
 970             copy(source, target, REPLACE_EXISTING,  new CopyOption() { });
 971         } catch (UnsupportedOperationException x) { }
 972         delete(source);
 973     }
 974 
 975     /**
 976      * Test copy from an input stream to a file
 977      */
 978     static void testCopyInputStreamToFile() throws IOException {
 979         testCopyInputStreamToFile(0);
 980         for (int i=0; i<100; i++) {
 981             testCopyInputStreamToFile(rand.nextInt(32000));
 982         }
 983 
 984         // FileAlreadyExistsException
 985         Path target = createTempFile("blah", null);
 986         try {
 987             InputStream in = new ByteArrayInputStream(new byte[0]);
 988             try {
 989                 copy(in, target);
 990                 throw new RuntimeException("FileAlreadyExistsException expected");
 991             } catch (FileAlreadyExistsException ignore) { }
 992         } finally {
 993             delete(target);
 994         }
 995         Path tmpdir = createTempDirectory("blah");
 996         try {
 997             if (TestUtil.supportsLinks(tmpdir)) {
 998                 Path link = createSymbolicLink(tmpdir.resolve("link"),
 999                                                   tmpdir.resolve("target"));
1000                 try {
1001                     InputStream in = new ByteArrayInputStream(new byte[0]);
1002                     try {
1003                         copy(in, link);
1004                         throw new RuntimeException("FileAlreadyExistsException expected");
1005                     } catch (FileAlreadyExistsException ignore) { }
1006                 } finally {
1007                     delete(link);
1008                 }
1009             }
1010         } finally {
1011             delete(tmpdir);
1012         }
1013 
1014 
1015         // nulls
1016         try {
1017             copy((InputStream)null, target);
1018             throw new RuntimeException("NullPointerException expected");
1019         } catch (NullPointerException ignore) { }
1020         try {
1021             copy(new ByteArrayInputStream(new byte[0]), (Path)null);
1022             throw new RuntimeException("NullPointerException expected");
1023         } catch (NullPointerException ignore) { }
1024     }
1025 
1026     static void testCopyInputStreamToFile(int size) throws IOException {
1027         Path tmpdir = createTempDirectory("blah");
1028         Path source = tmpdir.resolve("source");
1029         Path target = tmpdir.resolve("target");
1030         try {
1031             boolean testReplaceExisting = rand.nextBoolean();
1032 
1033             // create source file
1034             byte[] b = new byte[size];
1035             rand.nextBytes(b);
1036             write(source, b);
1037 
1038             // target file might already exist
1039             if (testReplaceExisting && rand.nextBoolean()) {
1040                 write(target, new byte[rand.nextInt(512)]);
1041             }
1042 
1043             // copy from stream to file
1044             InputStream in = new FileInputStream(source.toFile());
1045             try {
1046                 long n;
1047                 if (testReplaceExisting) {
1048                     n = copy(in, target, StandardCopyOption.REPLACE_EXISTING);
1049                 } else {
1050                     n = copy(in, target);
1051                 }
1052                 assertTrue(in.read() == -1);   // EOF
1053                 assertTrue(n == size);
1054                 assertTrue(size(target) == size);
1055             } finally {
1056                 in.close();
1057             }
1058 
1059             // check file
1060             byte[] read = readAllBytes(target);
1061             assertTrue(Arrays.equals(read, b));
1062 
1063         } finally {
1064             deleteIfExists(source);
1065             deleteIfExists(target);
1066             delete(tmpdir);
1067         }
1068     }
1069 
1070     /**
1071      * Test copy from file to output stream
1072      */
1073     static void testCopyFileToOuputStream() throws IOException {
1074         testCopyFileToOuputStream(0);
1075         for (int i=0; i<100; i++) {
1076             testCopyFileToOuputStream(rand.nextInt(32000));
1077         }
1078 
1079         // nulls
1080         try {
1081             copy((Path)null, new ByteArrayOutputStream());
1082             throw new RuntimeException("NullPointerException expected");
1083         } catch (NullPointerException ignore) { }
1084         try {
1085             Path source = createTempFile("blah", null);
1086             delete(source);
1087             copy(source, (OutputStream)null);
1088             throw new RuntimeException("NullPointerException expected");
1089         } catch (NullPointerException ignore) { }
1090     }
1091 
1092     static void testCopyFileToOuputStream(int size) throws IOException {
1093         Path source = createTempFile("blah", null);
1094         try {
1095             byte[] b = new byte[size];
1096             rand.nextBytes(b);
1097             write(source, b);
1098 
1099             ByteArrayOutputStream out = new ByteArrayOutputStream();
1100 
1101             long n = copy(source, out);
1102             assertTrue(n == size);
1103             assertTrue(out.size() == size);
1104 
1105             byte[] read = out.toByteArray();
1106             assertTrue(Arrays.equals(read, b));
1107 
1108             // check output stream is open
1109             out.write(0);
1110             assertTrue(out.size() == size+1);
1111         } finally {
1112             delete(source);
1113         }
1114     }
1115 
1116     static void assertTrue(boolean value) {
1117         if (!value)
1118             throw new RuntimeException("Assertion failed");
1119     }
1120 
1121     // computes simple hash of the given file
1122     static int computeHash(Path file) throws IOException {
1123         int h = 0;
1124 
1125         try (InputStream in = newInputStream(file)) {
1126             byte[] buf = new byte[1024];
1127             int n;
1128             do {
1129                 n = in.read(buf);
1130                 for (int i=0; i<n; i++) {
1131                     h = 31*h + (buf[i] & 0xff);
1132                 }
1133             } while (n > 0);
1134         }
1135         return h;
1136     }
1137 
1138     // create file of random size in given directory
1139     static Path createSourceFile(Path dir) throws IOException {
1140         String name = "source" + Integer.toString(rand.nextInt());
1141         Path file = dir.resolve(name);
1142         createFile(file);
1143         byte[] bytes = new byte[rand.nextInt(128*1024)];
1144         rand.nextBytes(bytes);
1145         try (OutputStream out = newOutputStream(file)) {
1146             out.write(bytes);
1147         }
1148         randomizeAttributes(file);
1149         return file;
1150     }
1151 
1152     // create directory in the given directory
1153     static Path createSourceDirectory(Path dir) throws IOException {
1154         String name = "sourcedir" + Integer.toString(rand.nextInt());
1155         Path subdir = dir.resolve(name);
1156         createDirectory(subdir);
1157         randomizeAttributes(subdir);
1158         return subdir;
1159     }
1160 
1161     // "randomize" the file attributes of the given file.
1162     static void randomizeAttributes(Path file) throws IOException {
1163         String os = System.getProperty("os.name");
1164         boolean isWindows = os.startsWith("Windows");
1165         boolean isUnix = os.equals("SunOS") || os.equals("Linux");
1166         boolean isDirectory = isDirectory(file, NOFOLLOW_LINKS);
1167 
1168         if (isUnix) {
1169             Set<PosixFilePermission> perms =
1170                 getPosixFilePermissions(file, NOFOLLOW_LINKS);
1171             PosixFilePermission[] toChange = {
1172                 PosixFilePermission.GROUP_READ,
1173                 PosixFilePermission.GROUP_WRITE,
1174                 PosixFilePermission.GROUP_EXECUTE,
1175                 PosixFilePermission.OTHERS_READ,
1176                 PosixFilePermission.OTHERS_WRITE,
1177                 PosixFilePermission.OTHERS_EXECUTE
1178             };
1179             for (PosixFilePermission perm: toChange) {
1180                 if (heads()) {
1181                     perms.add(perm);
1182                 } else {
1183                     perms.remove(perm);
1184                 }
1185             }
1186             setPosixFilePermissions(file, perms);
1187         }
1188 
1189         if (isWindows) {
1190             DosFileAttributeView view =
1191                 getFileAttributeView(file, DosFileAttributeView.class, NOFOLLOW_LINKS);
1192             // only set or unset the hidden attribute
1193             view.setHidden(heads());
1194         }
1195 
1196         boolean addUserDefinedFileAttributes = heads() &&
1197             getFileStore(file).supportsFileAttributeView("xattr");
1198 
1199         // remove this when copying a direcory copies its named streams
1200         if (isWindows && isDirectory) addUserDefinedFileAttributes = false;
1201 
1202         if (addUserDefinedFileAttributes) {
1203             UserDefinedFileAttributeView view =
1204                 getFileAttributeView(file, UserDefinedFileAttributeView.class);
1205             int n = rand.nextInt(16);
1206             while (n > 0) {
1207                 byte[] value = new byte[1 + rand.nextInt(100)];
1208                 view.write("user." + Integer.toString(n), ByteBuffer.wrap(value));
1209                 n--;
1210             }
1211         }
1212     }
1213 
1214     // create name for file in given directory
1215     static Path getTargetFile(Path dir) throws IOException {
1216         String name = "target" + Integer.toString(rand.nextInt());
1217         return dir.resolve(name);
1218     }
1219  }