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 { 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 fromCopyOptions(CopyOption... options)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 fromMoveOptions(CopyOption... options)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 copyDirectory(UnixPath source, UnixFileAttributes attrs, UnixPath target, Flags flags)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 copyFile(UnixPath source, UnixFileAttributes attrs, UnixPath target, Flags flags, long addressToPollForCancel)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 copyLink(UnixPath source, UnixFileAttributes attrs, UnixPath target, Flags flags)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 copySpecial(UnixPath source, UnixFileAttributes attrs, UnixPath target, Flags flags)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 move(UnixPath source, UnixPath target, CopyOption... options)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 copy(final UnixPath source, final UnixPath target, CopyOption... options)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 transfer(int dst, int src, long addressToPollForCancel)605 static native void transfer(int dst, int src, long addressToPollForCancel) 606 throws UnixException; 607 608 // Android-removed: Code to load native libraries, doesn't make sense on Android. 609 /* 610 static { 611 AccessController.doPrivileged(new PrivilegedAction<Void>() { 612 @Override 613 public Void run() { 614 System.loadLibrary("nio"); 615 return null; 616 }}); 617 } 618 */ 619 620 } 621