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