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