1 /* 2 * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.io.IOException; 29 import java.nio.file.AtomicMoveNotSupportedException; 30 import java.nio.file.CopyOption; 31 import java.nio.file.DirectoryNotEmptyException; 32 import java.nio.file.FileAlreadyExistsException; 33 import java.nio.file.LinkOption; 34 import java.nio.file.LinkPermission; 35 import java.nio.file.StandardCopyOption; 36 import java.util.concurrent.ExecutionException; 37 import java.util.concurrent.TimeUnit; 38 39 import static sun.nio.fs.UnixNativeDispatcher.*; 40 import static sun.nio.fs.UnixConstants.*; 41 42 43 /** 44 * Unix implementation of Path#copyTo and Path#moveTo methods. 45 */ 46 47 class UnixCopyFile { 48 private UnixCopyFile() { } 49 50 // The flags that control how a file is copied or moved 51 private static class Flags { 52 boolean replaceExisting; 53 boolean atomicMove; 54 boolean followLinks; 55 boolean interruptible; 56 57 // the attributes to copy 58 boolean copyBasicAttributes; 59 boolean copyPosixAttributes; 60 boolean copyNonPosixAttributes; 61 62 // flags that indicate if we should fail if attributes cannot be copied 63 boolean failIfUnableToCopyBasic; 64 boolean failIfUnableToCopyPosix; 65 boolean failIfUnableToCopyNonPosix; 66 67 static Flags fromCopyOptions(CopyOption... options) { 68 Flags flags = new Flags(); 69 flags.followLinks = true; 70 for (CopyOption option: options) { 71 if (option == StandardCopyOption.REPLACE_EXISTING) { 72 flags.replaceExisting = true; 73 continue; 74 } 75 if (option == LinkOption.NOFOLLOW_LINKS) { 76 flags.followLinks = false; 77 continue; 78 } 79 if (option == StandardCopyOption.COPY_ATTRIBUTES) { 80 // copy all attributes but only fail if basic attributes 81 // cannot be copied 82 flags.copyBasicAttributes = true; 83 flags.copyPosixAttributes = true; 84 flags.copyNonPosixAttributes = true; 85 flags.failIfUnableToCopyBasic = true; 86 continue; 87 } 88 if (ExtendedOptions.INTERRUPTIBLE.matches(option)) { 89 flags.interruptible = true; 90 continue; 91 } 92 if (option == null) 93 throw new NullPointerException(); 94 throw new UnsupportedOperationException("Unsupported copy option"); 95 } 96 return flags; 97 } 98 99 static Flags fromMoveOptions(CopyOption... options) { 100 Flags flags = new Flags(); 101 for (CopyOption option: options) { 102 if (option == StandardCopyOption.ATOMIC_MOVE) { 103 flags.atomicMove = true; 104 continue; 105 } 106 if (option == StandardCopyOption.REPLACE_EXISTING) { 107 flags.replaceExisting = true; 108 continue; 109 } 110 if (option == LinkOption.NOFOLLOW_LINKS) { 111 // ignore 112 continue; 113 } 114 if (option == null) 115 throw new NullPointerException(); 116 throw new UnsupportedOperationException("Unsupported copy option"); 117 } 118 119 // a move requires that all attributes be copied but only fail if 120 // the basic attributes cannot be copied 121 flags.copyBasicAttributes = true; 122 flags.copyPosixAttributes = true; 123 flags.copyNonPosixAttributes = true; 124 flags.failIfUnableToCopyBasic = true; 125 return flags; 126 } 127 } 128 129 // copy directory from source to target 130 private static void copyDirectory(UnixPath source, 131 UnixFileAttributes attrs, 132 UnixPath target, 133 Flags flags) 134 throws IOException 135 { 136 try { 137 mkdir(target, attrs.mode()); 138 } catch (UnixException x) { 139 x.rethrowAsIOException(target); 140 } 141 142 // no attributes to copy 143 if (!flags.copyBasicAttributes && 144 !flags.copyPosixAttributes && 145 !flags.copyNonPosixAttributes) return; 146 147 // open target directory if possible (this can fail when copying a 148 // directory for which we don't have read access). 149 int dfd = -1; 150 try { 151 dfd = open(target, O_RDONLY, 0); 152 } catch (UnixException x) { 153 // access to target directory required to copy named attributes 154 if (flags.copyNonPosixAttributes && flags.failIfUnableToCopyNonPosix) { 155 try { rmdir(target); } catch (UnixException ignore) { } 156 x.rethrowAsIOException(target); 157 } 158 } 159 160 boolean done = false; 161 try { 162 // copy owner/group/permissions 163 if (flags.copyPosixAttributes){ 164 try { 165 if (dfd >= 0) { 166 fchown(dfd, attrs.uid(), attrs.gid()); 167 fchmod(dfd, attrs.mode()); 168 } else { 169 chown(target, attrs.uid(), attrs.gid()); 170 chmod(target, attrs.mode()); 171 } 172 } catch (UnixException x) { 173 // unable to set owner/group 174 if (flags.failIfUnableToCopyPosix) 175 x.rethrowAsIOException(target); 176 } 177 } 178 // copy other attributes 179 if (flags.copyNonPosixAttributes && (dfd >= 0)) { 180 int sfd = -1; 181 try { 182 sfd = open(source, O_RDONLY, 0); 183 } catch (UnixException x) { 184 if (flags.failIfUnableToCopyNonPosix) 185 x.rethrowAsIOException(source); 186 } 187 if (sfd >= 0) { 188 source.getFileSystem().copyNonPosixAttributes(sfd, dfd); 189 close(sfd); 190 } 191 } 192 // copy time stamps last 193 if (flags.copyBasicAttributes) { 194 try { 195 if (dfd >= 0 && futimesSupported()) { 196 futimes(dfd, 197 attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), 198 attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); 199 } else { 200 utimes(target, 201 attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), 202 attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); 203 } 204 } catch (UnixException x) { 205 // unable to set times 206 if (flags.failIfUnableToCopyBasic) 207 x.rethrowAsIOException(target); 208 } 209 } 210 done = true; 211 } finally { 212 if (dfd >= 0) 213 close(dfd); 214 if (!done) { 215 // rollback 216 try { rmdir(target); } catch (UnixException ignore) { } 217 } 218 } 219 } 220 221 // copy regular file from source to target 222 private static void copyFile(UnixPath source, 223 UnixFileAttributes attrs, 224 UnixPath target, 225 Flags flags, 226 long addressToPollForCancel) 227 throws IOException 228 { 229 int fi = -1; 230 try { 231 fi = open(source, O_RDONLY, 0); 232 } catch (UnixException x) { 233 x.rethrowAsIOException(source); 234 } 235 236 try { 237 // open new file 238 int fo = -1; 239 try { 240 fo = open(target, 241 (O_WRONLY | 242 O_CREAT | 243 O_EXCL), 244 attrs.mode()); 245 } catch (UnixException x) { 246 x.rethrowAsIOException(target); 247 } 248 249 // set to true when file and attributes copied 250 boolean complete = false; 251 try { 252 // transfer bytes to target file 253 try { 254 transfer(fo, fi, addressToPollForCancel); 255 } catch (UnixException x) { 256 x.rethrowAsIOException(source, target); 257 } 258 // copy owner/permissions 259 if (flags.copyPosixAttributes) { 260 try { 261 fchown(fo, attrs.uid(), attrs.gid()); 262 fchmod(fo, attrs.mode()); 263 } catch (UnixException x) { 264 if (flags.failIfUnableToCopyPosix) 265 x.rethrowAsIOException(target); 266 } 267 } 268 // copy non POSIX attributes (depends on file system) 269 if (flags.copyNonPosixAttributes) { 270 source.getFileSystem().copyNonPosixAttributes(fi, fo); 271 } 272 // copy time attributes 273 if (flags.copyBasicAttributes) { 274 try { 275 if (futimesSupported()) { 276 futimes(fo, 277 attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), 278 attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); 279 } else { 280 utimes(target, 281 attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), 282 attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); 283 } 284 } catch (UnixException x) { 285 if (flags.failIfUnableToCopyBasic) 286 x.rethrowAsIOException(target); 287 } 288 } 289 complete = true; 290 } finally { 291 close(fo); 292 293 // copy of file or attributes failed so rollback 294 if (!complete) { 295 try { 296 unlink(target); 297 } catch (UnixException ignore) { } 298 } 299 } 300 } finally { 301 close(fi); 302 } 303 } 304 305 // copy symbolic link from source to target 306 private static void copyLink(UnixPath source, 307 UnixFileAttributes attrs, 308 UnixPath target, 309 Flags flags) 310 throws IOException 311 { 312 byte[] linktarget = null; 313 try { 314 linktarget = readlink(source); 315 } catch (UnixException x) { 316 x.rethrowAsIOException(source); 317 } 318 try { 319 symlink(linktarget, target); 320 321 if (flags.copyPosixAttributes) { 322 try { 323 lchown(target, attrs.uid(), attrs.gid()); 324 } catch (UnixException x) { 325 // ignore since link attributes not required to be copied 326 } 327 } 328 } catch (UnixException x) { 329 x.rethrowAsIOException(target); 330 } 331 } 332 333 // copy special file from source to target 334 private static void copySpecial(UnixPath source, 335 UnixFileAttributes attrs, 336 UnixPath target, 337 Flags flags) 338 throws IOException 339 { 340 try { 341 mknod(target, attrs.mode(), attrs.rdev()); 342 } catch (UnixException x) { 343 x.rethrowAsIOException(target); 344 } 345 boolean done = false; 346 try { 347 if (flags.copyPosixAttributes) { 348 try { 349 chown(target, attrs.uid(), attrs.gid()); 350 chmod(target, attrs.mode()); 351 } catch (UnixException x) { 352 if (flags.failIfUnableToCopyPosix) 353 x.rethrowAsIOException(target); 354 } 355 } 356 if (flags.copyBasicAttributes) { 357 try { 358 utimes(target, 359 attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), 360 attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); 361 } catch (UnixException x) { 362 if (flags.failIfUnableToCopyBasic) 363 x.rethrowAsIOException(target); 364 } 365 } 366 done = true; 367 } finally { 368 if (!done) { 369 try { unlink(target); } catch (UnixException ignore) { } 370 } 371 } 372 } 373 374 // throw a DirectoryNotEmpty exception if appropriate 375 static void ensureEmptyDir(UnixPath dir) throws IOException { 376 try { 377 long ptr = opendir(dir); 378 try (UnixDirectoryStream stream = 379 new UnixDirectoryStream(dir, ptr, e -> true)) { 380 if (stream.iterator().hasNext()) { 381 throw new DirectoryNotEmptyException( 382 dir.getPathForExceptionMessage()); 383 } 384 } 385 } catch (UnixException e) { 386 e.rethrowAsIOException(dir); 387 } 388 } 389 390 // move file from source to target 391 static void move(UnixPath source, UnixPath target, CopyOption... options) 392 throws IOException 393 { 394 // permission check 395 SecurityManager sm = System.getSecurityManager(); 396 if (sm != null) { 397 source.checkWrite(); 398 target.checkWrite(); 399 } 400 401 // translate options into flags 402 Flags flags = Flags.fromMoveOptions(options); 403 404 // handle atomic rename case 405 if (flags.atomicMove) { 406 try { 407 rename(source, target); 408 } catch (UnixException x) { 409 if (x.errno() == EXDEV) { 410 throw new AtomicMoveNotSupportedException( 411 source.getPathForExceptionMessage(), 412 target.getPathForExceptionMessage(), 413 x.errorString()); 414 } 415 x.rethrowAsIOException(source, target); 416 } 417 return; 418 } 419 420 // move using rename or copy+delete 421 UnixFileAttributes sourceAttrs = null; 422 UnixFileAttributes targetAttrs = null; 423 424 // get attributes of source file (don't follow links) 425 try { 426 sourceAttrs = UnixFileAttributes.get(source, false); 427 } catch (UnixException x) { 428 x.rethrowAsIOException(source); 429 } 430 431 // get attributes of target file (don't follow links) 432 try { 433 targetAttrs = UnixFileAttributes.get(target, false); 434 } catch (UnixException x) { 435 // ignore 436 } 437 boolean targetExists = (targetAttrs != null); 438 439 // if the target exists: 440 // 1. check if source and target are the same file 441 // 2. throw exception if REPLACE_EXISTING option is not set 442 // 3. delete target if REPLACE_EXISTING option set 443 if (targetExists) { 444 if (sourceAttrs.isSameFile(targetAttrs)) 445 return; // nothing to do as files are identical 446 if (!flags.replaceExisting) { 447 throw new FileAlreadyExistsException( 448 target.getPathForExceptionMessage()); 449 } 450 451 // attempt to delete target 452 try { 453 if (targetAttrs.isDirectory()) { 454 rmdir(target); 455 } else { 456 unlink(target); 457 } 458 } catch (UnixException x) { 459 // target is non-empty directory that can't be replaced. 460 if (targetAttrs.isDirectory() && 461 (x.errno() == EEXIST || x.errno() == ENOTEMPTY)) 462 { 463 throw new DirectoryNotEmptyException( 464 target.getPathForExceptionMessage()); 465 } 466 x.rethrowAsIOException(target); 467 } 468 } 469 470 // first try rename 471 try { 472 rename(source, target); 473 return; 474 } catch (UnixException x) { 475 if (x.errno() != EXDEV && x.errno() != EISDIR) { 476 x.rethrowAsIOException(source, target); 477 } 478 } 479 480 // copy source to target 481 if (sourceAttrs.isDirectory()) { 482 ensureEmptyDir(source); 483 copyDirectory(source, sourceAttrs, target, flags); 484 } else { 485 if (sourceAttrs.isSymbolicLink()) { 486 copyLink(source, sourceAttrs, target, flags); 487 } else { 488 if (sourceAttrs.isDevice()) { 489 copySpecial(source, sourceAttrs, target, flags); 490 } else { 491 copyFile(source, sourceAttrs, target, flags, 0L); 492 } 493 } 494 } 495 496 // delete source 497 try { 498 if (sourceAttrs.isDirectory()) { 499 rmdir(source); 500 } else { 501 unlink(source); 502 } 503 } catch (UnixException x) { 504 // file was copied but unable to unlink the source file so attempt 505 // to remove the target and throw a reasonable exception 506 try { 507 if (sourceAttrs.isDirectory()) { 508 rmdir(target); 509 } else { 510 unlink(target); 511 } 512 } catch (UnixException ignore) { } 513 514 if (sourceAttrs.isDirectory() && 515 (x.errno() == EEXIST || x.errno() == ENOTEMPTY)) 516 { 517 throw new DirectoryNotEmptyException( 518 source.getPathForExceptionMessage()); 519 } 520 x.rethrowAsIOException(source); 521 } 522 } 523 524 // copy file from source to target 525 static void copy(final UnixPath source, 526 final UnixPath target, 527 CopyOption... options) throws IOException 528 { 529 // permission checks 530 SecurityManager sm = System.getSecurityManager(); 531 if (sm != null) { 532 source.checkRead(); 533 target.checkWrite(); 534 } 535 536 // translate options into flags 537 final Flags flags = Flags.fromCopyOptions(options); 538 539 UnixFileAttributes sourceAttrs = null; 540 UnixFileAttributes targetAttrs = null; 541 542 // get attributes of source file 543 try { 544 sourceAttrs = UnixFileAttributes.get(source, flags.followLinks); 545 } catch (UnixException x) { 546 x.rethrowAsIOException(source); 547 } 548 549 // if source file is symbolic link then we must check LinkPermission 550 if (sm != null && sourceAttrs.isSymbolicLink()) { 551 sm.checkPermission(new LinkPermission("symbolic")); 552 } 553 554 // get attributes of target file (don't follow links) 555 try { 556 targetAttrs = UnixFileAttributes.get(target, false); 557 } catch (UnixException x) { 558 // ignore 559 } 560 boolean targetExists = (targetAttrs != null); 561 562 // if the target exists: 563 // 1. check if source and target are the same file 564 // 2. throw exception if REPLACE_EXISTING option is not set 565 // 3. try to unlink the target 566 if (targetExists) { 567 if (sourceAttrs.isSameFile(targetAttrs)) 568 return; // nothing to do as files are identical 569 if (!flags.replaceExisting) 570 throw new FileAlreadyExistsException( 571 target.getPathForExceptionMessage()); 572 try { 573 if (targetAttrs.isDirectory()) { 574 rmdir(target); 575 } else { 576 unlink(target); 577 } 578 } catch (UnixException x) { 579 // target is non-empty directory that can't be replaced. 580 if (targetAttrs.isDirectory() && 581 (x.errno() == EEXIST || x.errno() == ENOTEMPTY)) 582 { 583 throw new DirectoryNotEmptyException( 584 target.getPathForExceptionMessage()); 585 } 586 x.rethrowAsIOException(target); 587 } 588 } 589 590 // do the copy 591 if (sourceAttrs.isDirectory()) { 592 copyDirectory(source, sourceAttrs, target, flags); 593 return; 594 } 595 if (sourceAttrs.isSymbolicLink()) { 596 copyLink(source, sourceAttrs, target, flags); 597 return; 598 } 599 if (!flags.interruptible) { 600 // non-interruptible file copy 601 copyFile(source, sourceAttrs, target, flags, 0L); 602 return; 603 } 604 605 // interruptible file copy 606 final UnixFileAttributes attrsToCopy = sourceAttrs; 607 Cancellable copyTask = new Cancellable() { 608 @Override public void implRun() throws IOException { 609 copyFile(source, attrsToCopy, target, flags, 610 addressToPollForCancel()); 611 } 612 }; 613 try { 614 Cancellable.runInterruptibly(copyTask); 615 } catch (ExecutionException e) { 616 Throwable t = e.getCause(); 617 if (t instanceof IOException) 618 throw (IOException)t; 619 throw new IOException(t); 620 } 621 } 622 623 // -- native methods -- 624 625 static native void transfer(int dst, int src, long addressToPollForCancel) 626 throws UnixException; 627 628 static { 629 jdk.internal.access.SharedSecrets.getJavaLangAccess().loadLibrary("nio"); 630 } 631 632 }