Path: blob/master/src/java.base/unix/classes/sun/nio/fs/UnixCopyFile.java
41137 views
/*1* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.fs;2627import java.io.IOException;28import java.nio.file.AtomicMoveNotSupportedException;29import java.nio.file.CopyOption;30import java.nio.file.DirectoryNotEmptyException;31import java.nio.file.FileAlreadyExistsException;32import java.nio.file.LinkOption;33import java.nio.file.LinkPermission;34import java.nio.file.StandardCopyOption;35import java.util.concurrent.ExecutionException;36import java.util.concurrent.TimeUnit;3738import static sun.nio.fs.UnixNativeDispatcher.*;39import static sun.nio.fs.UnixConstants.*;404142/**43* Unix implementation of Path#copyTo and Path#moveTo methods.44*/4546class UnixCopyFile {47private UnixCopyFile() { }4849// The flags that control how a file is copied or moved50private static class Flags {51boolean replaceExisting;52boolean atomicMove;53boolean followLinks;54boolean interruptible;5556// the attributes to copy57boolean copyBasicAttributes;58boolean copyPosixAttributes;59boolean copyNonPosixAttributes;6061// flags that indicate if we should fail if attributes cannot be copied62boolean failIfUnableToCopyBasic;63boolean failIfUnableToCopyPosix;64boolean failIfUnableToCopyNonPosix;6566static Flags fromCopyOptions(CopyOption... options) {67Flags flags = new Flags();68flags.followLinks = true;69for (CopyOption option: options) {70if (option == StandardCopyOption.REPLACE_EXISTING) {71flags.replaceExisting = true;72continue;73}74if (option == LinkOption.NOFOLLOW_LINKS) {75flags.followLinks = false;76continue;77}78if (option == StandardCopyOption.COPY_ATTRIBUTES) {79// copy all attributes but only fail if basic attributes80// cannot be copied81flags.copyBasicAttributes = true;82flags.copyPosixAttributes = true;83flags.copyNonPosixAttributes = true;84flags.failIfUnableToCopyBasic = true;85continue;86}87if (ExtendedOptions.INTERRUPTIBLE.matches(option)) {88flags.interruptible = true;89continue;90}91if (option == null)92throw new NullPointerException();93throw new UnsupportedOperationException("Unsupported copy option");94}95return flags;96}9798static Flags fromMoveOptions(CopyOption... options) {99Flags flags = new Flags();100for (CopyOption option: options) {101if (option == StandardCopyOption.ATOMIC_MOVE) {102flags.atomicMove = true;103continue;104}105if (option == StandardCopyOption.REPLACE_EXISTING) {106flags.replaceExisting = true;107continue;108}109if (option == LinkOption.NOFOLLOW_LINKS) {110// ignore111continue;112}113if (option == null)114throw new NullPointerException();115throw new UnsupportedOperationException("Unsupported copy option");116}117118// a move requires that all attributes be copied but only fail if119// the basic attributes cannot be copied120flags.copyBasicAttributes = true;121flags.copyPosixAttributes = true;122flags.copyNonPosixAttributes = true;123flags.failIfUnableToCopyBasic = true;124return flags;125}126}127128// copy directory from source to target129private static void copyDirectory(UnixPath source,130UnixFileAttributes attrs,131UnixPath target,132Flags flags)133throws IOException134{135try {136mkdir(target, attrs.mode());137} catch (UnixException x) {138x.rethrowAsIOException(target);139}140141// no attributes to copy142if (!flags.copyBasicAttributes &&143!flags.copyPosixAttributes &&144!flags.copyNonPosixAttributes) return;145146// open target directory if possible (this can fail when copying a147// directory for which we don't have read access).148int dfd = -1;149try {150dfd = open(target, O_RDONLY, 0);151} catch (UnixException x) {152// access to target directory required to copy named attributes153if (flags.copyNonPosixAttributes && flags.failIfUnableToCopyNonPosix) {154try { rmdir(target); } catch (UnixException ignore) { }155x.rethrowAsIOException(target);156}157}158159boolean done = false;160try {161// copy owner/group/permissions162if (flags.copyPosixAttributes){163try {164if (dfd >= 0) {165fchown(dfd, attrs.uid(), attrs.gid());166fchmod(dfd, attrs.mode());167} else {168chown(target, attrs.uid(), attrs.gid());169chmod(target, attrs.mode());170}171} catch (UnixException x) {172// unable to set owner/group173if (flags.failIfUnableToCopyPosix)174x.rethrowAsIOException(target);175}176}177// copy other attributes178if (flags.copyNonPosixAttributes && (dfd >= 0)) {179int sfd = -1;180try {181sfd = open(source, O_RDONLY, 0);182} catch (UnixException x) {183if (flags.failIfUnableToCopyNonPosix)184x.rethrowAsIOException(source);185}186if (sfd >= 0) {187source.getFileSystem().copyNonPosixAttributes(sfd, dfd);188close(sfd);189}190}191// copy time stamps last192if (flags.copyBasicAttributes) {193try {194if (dfd >= 0 && futimesSupported()) {195futimes(dfd,196attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),197attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));198} else {199utimes(target,200attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),201attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));202}203} catch (UnixException x) {204// unable to set times205if (flags.failIfUnableToCopyBasic)206x.rethrowAsIOException(target);207}208}209done = true;210} finally {211if (dfd >= 0)212close(dfd);213if (!done) {214// rollback215try { rmdir(target); } catch (UnixException ignore) { }216}217}218}219220// copy regular file from source to target221private static void copyFile(UnixPath source,222UnixFileAttributes attrs,223UnixPath target,224Flags flags,225long addressToPollForCancel)226throws IOException227{228int fi = -1;229try {230fi = open(source, O_RDONLY, 0);231} catch (UnixException x) {232x.rethrowAsIOException(source);233}234235try {236// open new file237int fo = -1;238try {239fo = open(target,240(O_WRONLY |241O_CREAT |242O_EXCL),243attrs.mode());244} catch (UnixException x) {245x.rethrowAsIOException(target);246}247248// set to true when file and attributes copied249boolean complete = false;250try {251// transfer bytes to target file252try {253transfer(fo, fi, addressToPollForCancel);254} catch (UnixException x) {255x.rethrowAsIOException(source, target);256}257// copy owner/permissions258if (flags.copyPosixAttributes) {259try {260fchown(fo, attrs.uid(), attrs.gid());261fchmod(fo, attrs.mode());262} catch (UnixException x) {263if (flags.failIfUnableToCopyPosix)264x.rethrowAsIOException(target);265}266}267// copy non POSIX attributes (depends on file system)268if (flags.copyNonPosixAttributes) {269source.getFileSystem().copyNonPosixAttributes(fi, fo);270}271// copy time attributes272if (flags.copyBasicAttributes) {273try {274if (futimesSupported()) {275futimes(fo,276attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),277attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));278} else {279utimes(target,280attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),281attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));282}283} catch (UnixException x) {284if (flags.failIfUnableToCopyBasic)285x.rethrowAsIOException(target);286}287}288complete = true;289} finally {290close(fo);291292// copy of file or attributes failed so rollback293if (!complete) {294try {295unlink(target);296} catch (UnixException ignore) { }297}298}299} finally {300close(fi);301}302}303304// copy symbolic link from source to target305private static void copyLink(UnixPath source,306UnixFileAttributes attrs,307UnixPath target,308Flags flags)309throws IOException310{311byte[] linktarget = null;312try {313linktarget = readlink(source);314} catch (UnixException x) {315x.rethrowAsIOException(source);316}317try {318symlink(linktarget, target);319320if (flags.copyPosixAttributes) {321try {322lchown(target, attrs.uid(), attrs.gid());323} catch (UnixException x) {324// ignore since link attributes not required to be copied325}326}327} catch (UnixException x) {328x.rethrowAsIOException(target);329}330}331332// copy special file from source to target333private static void copySpecial(UnixPath source,334UnixFileAttributes attrs,335UnixPath target,336Flags flags)337throws IOException338{339try {340mknod(target, attrs.mode(), attrs.rdev());341} catch (UnixException x) {342x.rethrowAsIOException(target);343}344boolean done = false;345try {346if (flags.copyPosixAttributes) {347try {348chown(target, attrs.uid(), attrs.gid());349chmod(target, attrs.mode());350} catch (UnixException x) {351if (flags.failIfUnableToCopyPosix)352x.rethrowAsIOException(target);353}354}355if (flags.copyBasicAttributes) {356try {357utimes(target,358attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),359attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));360} catch (UnixException x) {361if (flags.failIfUnableToCopyBasic)362x.rethrowAsIOException(target);363}364}365done = true;366} finally {367if (!done) {368try { unlink(target); } catch (UnixException ignore) { }369}370}371}372373// throw a DirectoryNotEmpty exception if appropriate374static void ensureEmptyDir(UnixPath dir) throws IOException {375try {376long ptr = opendir(dir);377try (UnixDirectoryStream stream =378new UnixDirectoryStream(dir, ptr, e -> true)) {379if (stream.iterator().hasNext()) {380throw new DirectoryNotEmptyException(381dir.getPathForExceptionMessage());382}383}384} catch (UnixException e) {385e.rethrowAsIOException(dir);386}387}388389// move file from source to target390static void move(UnixPath source, UnixPath target, CopyOption... options)391throws IOException392{393// permission check394@SuppressWarnings("removal")395SecurityManager sm = System.getSecurityManager();396if (sm != null) {397source.checkWrite();398target.checkWrite();399}400401// translate options into flags402Flags flags = Flags.fromMoveOptions(options);403404// handle atomic rename case405if (flags.atomicMove) {406try {407rename(source, target);408} catch (UnixException x) {409if (x.errno() == EXDEV) {410throw new AtomicMoveNotSupportedException(411source.getPathForExceptionMessage(),412target.getPathForExceptionMessage(),413x.errorString());414}415x.rethrowAsIOException(source, target);416}417return;418}419420// move using rename or copy+delete421UnixFileAttributes sourceAttrs = null;422UnixFileAttributes targetAttrs = null;423424// get attributes of source file (don't follow links)425try {426sourceAttrs = UnixFileAttributes.get(source, false);427} catch (UnixException x) {428x.rethrowAsIOException(source);429}430431// get attributes of target file (don't follow links)432try {433targetAttrs = UnixFileAttributes.get(target, false);434} catch (UnixException x) {435// ignore436}437boolean targetExists = (targetAttrs != null);438439// if the target exists:440// 1. check if source and target are the same file441// 2. throw exception if REPLACE_EXISTING option is not set442// 3. delete target if REPLACE_EXISTING option set443if (targetExists) {444if (sourceAttrs.isSameFile(targetAttrs))445return; // nothing to do as files are identical446if (!flags.replaceExisting) {447throw new FileAlreadyExistsException(448target.getPathForExceptionMessage());449}450451// attempt to delete target452try {453if (targetAttrs.isDirectory()) {454rmdir(target);455} else {456unlink(target);457}458} catch (UnixException x) {459// target is non-empty directory that can't be replaced.460if (targetAttrs.isDirectory() &&461(x.errno() == EEXIST || x.errno() == ENOTEMPTY))462{463throw new DirectoryNotEmptyException(464target.getPathForExceptionMessage());465}466x.rethrowAsIOException(target);467}468}469470// first try rename471try {472rename(source, target);473return;474} catch (UnixException x) {475if (x.errno() != EXDEV && x.errno() != EISDIR) {476x.rethrowAsIOException(source, target);477}478}479480// copy source to target481if (sourceAttrs.isDirectory()) {482ensureEmptyDir(source);483copyDirectory(source, sourceAttrs, target, flags);484} else {485if (sourceAttrs.isSymbolicLink()) {486copyLink(source, sourceAttrs, target, flags);487} else {488if (sourceAttrs.isDevice()) {489copySpecial(source, sourceAttrs, target, flags);490} else {491copyFile(source, sourceAttrs, target, flags, 0L);492}493}494}495496// delete source497try {498if (sourceAttrs.isDirectory()) {499rmdir(source);500} else {501unlink(source);502}503} catch (UnixException x) {504// file was copied but unable to unlink the source file so attempt505// to remove the target and throw a reasonable exception506try {507if (sourceAttrs.isDirectory()) {508rmdir(target);509} else {510unlink(target);511}512} catch (UnixException ignore) { }513514if (sourceAttrs.isDirectory() &&515(x.errno() == EEXIST || x.errno() == ENOTEMPTY))516{517throw new DirectoryNotEmptyException(518source.getPathForExceptionMessage());519}520x.rethrowAsIOException(source);521}522}523524// copy file from source to target525static void copy(final UnixPath source,526final UnixPath target,527CopyOption... options) throws IOException528{529// permission checks530@SuppressWarnings("removal")531SecurityManager sm = System.getSecurityManager();532if (sm != null) {533source.checkRead();534target.checkWrite();535}536537// translate options into flags538final Flags flags = Flags.fromCopyOptions(options);539540UnixFileAttributes sourceAttrs = null;541UnixFileAttributes targetAttrs = null;542543// get attributes of source file544try {545sourceAttrs = UnixFileAttributes.get(source, flags.followLinks);546} catch (UnixException x) {547x.rethrowAsIOException(source);548}549550// if source file is symbolic link then we must check LinkPermission551if (sm != null && sourceAttrs.isSymbolicLink()) {552sm.checkPermission(new LinkPermission("symbolic"));553}554555// get attributes of target file (don't follow links)556try {557targetAttrs = UnixFileAttributes.get(target, false);558} catch (UnixException x) {559// ignore560}561boolean targetExists = (targetAttrs != null);562563// if the target exists:564// 1. check if source and target are the same file565// 2. throw exception if REPLACE_EXISTING option is not set566// 3. try to unlink the target567if (targetExists) {568if (sourceAttrs.isSameFile(targetAttrs))569return; // nothing to do as files are identical570if (!flags.replaceExisting)571throw new FileAlreadyExistsException(572target.getPathForExceptionMessage());573try {574if (targetAttrs.isDirectory()) {575rmdir(target);576} else {577unlink(target);578}579} catch (UnixException x) {580// target is non-empty directory that can't be replaced.581if (targetAttrs.isDirectory() &&582(x.errno() == EEXIST || x.errno() == ENOTEMPTY))583{584throw new DirectoryNotEmptyException(585target.getPathForExceptionMessage());586}587x.rethrowAsIOException(target);588}589}590591// do the copy592if (sourceAttrs.isDirectory()) {593copyDirectory(source, sourceAttrs, target, flags);594return;595}596if (sourceAttrs.isSymbolicLink()) {597copyLink(source, sourceAttrs, target, flags);598return;599}600if (!flags.interruptible) {601// non-interruptible file copy602copyFile(source, sourceAttrs, target, flags, 0L);603return;604}605606// interruptible file copy607final UnixFileAttributes attrsToCopy = sourceAttrs;608Cancellable copyTask = new Cancellable() {609@Override public void implRun() throws IOException {610copyFile(source, attrsToCopy, target, flags,611addressToPollForCancel());612}613};614try {615Cancellable.runInterruptibly(copyTask);616} catch (ExecutionException e) {617Throwable t = e.getCause();618if (t instanceof IOException)619throw (IOException)t;620throw new IOException(t);621}622}623624// -- native methods --625626static native void transfer(int dst, int src, long addressToPollForCancel)627throws UnixException;628629static {630jdk.internal.loader.BootLoader.loadLibrary("nio");631}632633}634635636