jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java
changeset 2057 3acf8e5e2ca0
child 3065 452aaa2899fc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java	Sun Feb 15 12:25:54 2009 +0000
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.concurrent.ExecutionException;
+import com.sun.nio.file.ExtendedCopyOption;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+
+/**
+ * Unix implementation of Path#copyTo and Path#moveTo methods.
+ */
+
+class UnixCopyFile {
+    private UnixCopyFile() {  }
+
+    // The flags that control how a file is copied or moved
+    private static class Flags {
+        boolean replaceExisting;
+        boolean atomicMove;
+        boolean followLinks;
+        boolean interruptible;
+
+        // the attributes to copy
+        boolean copyBasicAttributes;
+        boolean copyPosixAttributes;
+        boolean copyNonPosixAttributes;
+
+        // flags that indicate if we should fail if attributes cannot be copied
+        boolean failIfUnableToCopyBasic;
+        boolean failIfUnableToCopyPosix;
+        boolean failIfUnableToCopyNonPosix;
+
+        static Flags fromCopyOptions(CopyOption... options) {
+            Flags flags = new Flags();
+            flags.followLinks = true;
+            for (CopyOption option: options) {
+                if (option == StandardCopyOption.REPLACE_EXISTING) {
+                    flags.replaceExisting = true;
+                    continue;
+                }
+                if (option == LinkOption.NOFOLLOW_LINKS) {
+                    flags.followLinks = false;
+                    continue;
+                }
+                if (option == StandardCopyOption.COPY_ATTRIBUTES) {
+                    // copy all attributes but only fail if basic attributes
+                    // cannot be copied
+                    flags.copyBasicAttributes = true;
+                    flags.copyPosixAttributes = true;
+                    flags.copyNonPosixAttributes = true;
+                    flags.failIfUnableToCopyBasic = true;
+                    continue;
+                }
+                if (option == ExtendedCopyOption.INTERRUPTIBLE) {
+                    flags.interruptible = true;
+                    continue;
+                }
+                if (option == null)
+                    throw new NullPointerException();
+                throw new UnsupportedOperationException("Unsupported copy option");
+            }
+            return flags;
+        }
+
+        static Flags fromMoveOptions(CopyOption... options) {
+            Flags flags = new Flags();
+            for (CopyOption option: options) {
+                if (option == StandardCopyOption.ATOMIC_MOVE) {
+                    flags.atomicMove = true;
+                    continue;
+                }
+                if (option == StandardCopyOption.REPLACE_EXISTING) {
+                    flags.replaceExisting = true;
+                    continue;
+                }
+                if (option == LinkOption.NOFOLLOW_LINKS) {
+                    // ignore
+                    continue;
+                }
+                if (option == null)
+                    throw new NullPointerException();
+                throw new UnsupportedOperationException("Unsupported copy option");
+            }
+
+            // a move requires that all attributes be copied but only fail if
+            // the basic attributes cannot be copied
+            flags.copyBasicAttributes = true;
+            flags.copyPosixAttributes = true;
+            flags.copyNonPosixAttributes = true;
+            flags.failIfUnableToCopyBasic = true;
+            return flags;
+        }
+    }
+
+    // copy directory from source to target
+    private static void copyDirectory(UnixPath source,
+                                      UnixFileAttributes attrs,
+                                      UnixPath target,
+                                      Flags flags)
+        throws IOException
+    {
+        try {
+            mkdir(target, attrs.mode());
+        } catch (UnixException x) {
+            x.rethrowAsIOException(target);
+        }
+
+        // no attributes to copy
+        if (!flags.copyBasicAttributes &&
+            !flags.copyPosixAttributes &&
+            !flags.copyNonPosixAttributes) return;
+
+        // open target directory if possible (this can fail when copying a
+        // directory for which we don't have read access).
+        int dfd = -1;
+        try {
+            dfd = open(target, O_RDONLY, 0);
+        } catch (UnixException x) {
+            // access to target directory required to copy named attributes
+            if (flags.copyNonPosixAttributes && flags.failIfUnableToCopyNonPosix) {
+                try { rmdir(target); } catch (UnixException ignore) { }
+                x.rethrowAsIOException(target);
+            }
+        }
+
+        boolean done = false;
+        try {
+            // copy owner/group/permissions
+            if (flags.copyPosixAttributes){
+                try {
+                    if (dfd >= 0) {
+                        fchown(dfd, attrs.uid(), attrs.gid());
+                        fchmod(dfd, attrs.mode());
+                    } else {
+                        chown(target, attrs.uid(), attrs.gid());
+                        chmod(target, attrs.mode());
+                    }
+                } catch (UnixException x) {
+                    // unable to set owner/group
+                    if (flags.failIfUnableToCopyPosix)
+                        x.rethrowAsIOException(target);
+                }
+            }
+            // copy other attributes
+            if (flags.copyNonPosixAttributes && (dfd >= 0)) {
+                int sfd = -1;
+                try {
+                    sfd = open(source, O_RDONLY, 0);
+                } catch (UnixException x) {
+                    if (flags.failIfUnableToCopyNonPosix)
+                        x.rethrowAsIOException(source);
+                }
+                if (sfd >= 0) {
+                    source.getFileSystem().copyNonPosixAttributes(sfd, dfd);
+                    close(sfd);
+                }
+            }
+            // copy time stamps last
+            if (flags.copyBasicAttributes) {
+                try {
+                    if (dfd >= 0) {
+                        futimes(dfd, attrs.lastAccessTime(),
+                            attrs.lastModifiedTime());
+                    } else {
+                        utimes(target, attrs.lastAccessTime(),
+                            attrs.lastModifiedTime());
+                    }
+                } catch (UnixException x) {
+                    // unable to set times
+                    if (flags.failIfUnableToCopyBasic)
+                        x.rethrowAsIOException(target);
+                }
+            }
+            done = true;
+        } finally {
+            if (dfd >= 0)
+                close(dfd);
+            if (!done) {
+                // rollback
+                try { rmdir(target); } catch (UnixException ignore) { }
+            }
+        }
+    }
+
+    // copy regular file from source to target
+    private static void copyFile(UnixPath source,
+                                 UnixFileAttributes attrs,
+                                 UnixPath  target,
+                                 Flags flags,
+                                 long addressToPollForCancel)
+        throws IOException
+    {
+        int fi = -1;
+        try {
+            fi = open(source, O_RDONLY, 0);
+        } catch (UnixException x) {
+            x.rethrowAsIOException(source);
+        }
+
+        try {
+            // open new file
+            int fo = -1;
+            try {
+                fo = open(target,
+                           (O_WRONLY |
+                            O_CREAT |
+                            O_TRUNC),
+                           attrs.mode());
+            } catch (UnixException x) {
+                x.rethrowAsIOException(target);
+            }
+
+            // set to true when file and attributes copied
+            boolean complete = false;
+            try {
+                // transfer bytes to target file
+                try {
+                    transfer(fo, fi, addressToPollForCancel);
+                } catch (UnixException x) {
+                    x.rethrowAsIOException(source, target);
+                }
+                // copy owner/permissions
+                if (flags.copyPosixAttributes) {
+                    try {
+                        fchown(fo, attrs.uid(), attrs.gid());
+                        fchmod(fo, attrs.mode());
+                    } catch (UnixException x) {
+                        if (flags.failIfUnableToCopyPosix)
+                            x.rethrowAsIOException(target);
+                    }
+                }
+                // copy non POSIX attributes (depends on file system)
+                if (flags.copyNonPosixAttributes) {
+                    source.getFileSystem().copyNonPosixAttributes(fi, fo);
+                }
+                // copy time attributes
+                if (flags.copyBasicAttributes) {
+                    try {
+                        futimes(fo, attrs.lastAccessTime(), attrs.lastModifiedTime());
+                    } catch (UnixException x) {
+                        if (flags.failIfUnableToCopyBasic)
+                            x.rethrowAsIOException(target);
+                    }
+                }
+                complete = true;
+            } finally {
+                close(fo);
+
+                // copy of file or attributes failed so rollback
+                if (!complete) {
+                    try {
+                        unlink(target);
+                    } catch (UnixException ignore) { }
+                }
+            }
+        } finally {
+            close(fi);
+        }
+    }
+
+    // copy symbolic link from source to target
+    private static void copyLink(UnixPath source,
+                                 UnixFileAttributes attrs,
+                                 UnixPath  target,
+                                 Flags flags)
+        throws IOException
+    {
+        byte[] linktarget = null;
+        try {
+            linktarget = readlink(source);
+        } catch (UnixException x) {
+            x.rethrowAsIOException(source);
+        }
+        try {
+            symlink(linktarget, target);
+
+            if (flags.copyPosixAttributes) {
+                try {
+                    lchown(target, attrs.uid(), attrs.gid());
+                } catch (UnixException x) {
+                    // ignore since link attributes not required to be copied
+                }
+            }
+        } catch (UnixException x) {
+            x.rethrowAsIOException(target);
+        }
+    }
+
+    // copy special file from source to target
+    private static void copySpecial(UnixPath source,
+                                    UnixFileAttributes attrs,
+                                    UnixPath  target,
+                                    Flags flags)
+        throws IOException
+    {
+        try {
+            mknod(target, attrs.mode(), attrs.rdev());
+        } catch (UnixException x) {
+            x.rethrowAsIOException(target);
+        }
+        boolean done = false;
+        try {
+            if (flags.copyPosixAttributes) {
+                try {
+                    chown(target, attrs.uid(), attrs.gid());
+                    chmod(target, attrs.mode());
+                } catch (UnixException x) {
+                    if (flags.failIfUnableToCopyPosix)
+                        x.rethrowAsIOException(target);
+                }
+            }
+            if (flags.copyBasicAttributes) {
+                try {
+                    utimes(target, attrs.lastAccessTime(), attrs.lastModifiedTime());
+                } catch (UnixException x) {
+                    if (flags.failIfUnableToCopyBasic)
+                        x.rethrowAsIOException(target);
+                }
+            }
+            done = true;
+        } finally {
+            if (!done) {
+                try { unlink(target); } catch (UnixException ignore) { }
+            }
+        }
+    }
+
+    // move file from source to target
+    static void move(UnixPath source, UnixPath target, CopyOption... options)
+        throws IOException
+    {
+        // permission check
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            source.checkWrite();
+            target.checkWrite();
+        }
+
+        // translate options into flags
+        Flags flags = Flags.fromMoveOptions(options);
+
+        // handle atomic rename case
+        if (flags.atomicMove) {
+            try {
+                rename(source, target);
+            } catch (UnixException x) {
+                if (x.errno() == EXDEV) {
+                    throw new AtomicMoveNotSupportedException(
+                        source.getPathForExecptionMessage(),
+                        target.getPathForExecptionMessage(),
+                        x.errorString());
+                }
+                x.rethrowAsIOException(source, target);
+            }
+            return;
+        }
+
+        // move using rename or copy+delete
+        UnixFileAttributes sourceAttrs = null;
+        UnixFileAttributes targetAttrs = null;
+
+        // get attributes of source file (don't follow links)
+        try {
+            sourceAttrs = UnixFileAttributes.get(source, false);
+        } catch (UnixException x) {
+            x.rethrowAsIOException(source);
+        }
+
+        // get attributes of target file (don't follow links)
+        try {
+            targetAttrs = UnixFileAttributes.get(target, false);
+        } catch (UnixException x) {
+            // ignore
+        }
+        boolean targetExists = (targetAttrs != null);
+
+        // if the target exists:
+        // 1. check if source and target are the same file
+        // 2. throw exception if REPLACE_EXISTING option is not set
+        // 3. delete target if REPLACE_EXISTING option set
+        if (targetExists) {
+            if (sourceAttrs.isSameFile(targetAttrs))
+                return;  // nothing to do as files are identical
+            if (!flags.replaceExisting) {
+                throw new FileAlreadyExistsException(
+                    target.getPathForExecptionMessage());
+            }
+
+            // attempt to delete target
+            try {
+                if (targetAttrs.isDirectory()) {
+                    rmdir(target);
+                } else {
+                    unlink(target);
+                }
+            } catch (UnixException x) {
+                // target is non-empty directory that can't be replaced.
+                if (targetAttrs.isDirectory() &&
+                   (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+                {
+                    throw new FileAlreadyExistsException(
+                        source.getPathForExecptionMessage(),
+                        target.getPathForExecptionMessage(),
+                        x.getMessage());
+                }
+                x.rethrowAsIOException(target);
+            }
+        }
+
+        // first try rename
+        try {
+            rename(source, target);
+            return;
+        } catch (UnixException x) {
+            if (x.errno() != EXDEV && x.errno() != EISDIR) {
+                x.rethrowAsIOException(source, target);
+            }
+        }
+
+        // copy source to target
+        if (sourceAttrs.isDirectory()) {
+            copyDirectory(source, sourceAttrs, target, flags);
+        } else {
+            if (sourceAttrs.isSymbolicLink()) {
+                copyLink(source, sourceAttrs, target, flags);
+            } else {
+                if (sourceAttrs.isDevice()) {
+                    copySpecial(source, sourceAttrs, target, flags);
+                } else {
+                    copyFile(source, sourceAttrs, target, flags, 0L);
+                }
+            }
+        }
+
+        // delete source
+        try {
+            if (sourceAttrs.isDirectory()) {
+                rmdir(source);
+            } else {
+                unlink(source);
+            }
+        } catch (UnixException x) {
+            // file was copied but unable to unlink the source file so attempt
+            // to remove the target and throw a reasonable exception
+            try {
+                if (sourceAttrs.isDirectory()) {
+                    rmdir(target);
+                } else {
+                    unlink(target);
+                }
+            } catch (UnixException ignore) { }
+
+            if (sourceAttrs.isDirectory() &&
+                (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+            {
+                throw new DirectoryNotEmptyException(
+                    source.getPathForExecptionMessage());
+            }
+            x.rethrowAsIOException(source);
+        }
+    }
+
+    // copy file from source to target
+    static void copy(final UnixPath source,
+                     final UnixPath target,
+                     CopyOption... options) throws IOException
+    {
+        // permission checks
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            source.checkRead();
+            target.checkWrite();
+        }
+
+        // translate options into flags
+        final Flags flags = Flags.fromCopyOptions(options);
+
+        UnixFileAttributes sourceAttrs = null;
+        UnixFileAttributes targetAttrs = null;
+
+        // get attributes of source file
+        try {
+            sourceAttrs = UnixFileAttributes.get(source, flags.followLinks);
+        } catch (UnixException x) {
+            x.rethrowAsIOException(source);
+        }
+
+        // if source file is symbolic link then we must check LinkPermission
+        if (sm != null && sourceAttrs.isSymbolicLink()) {
+            sm.checkPermission(new LinkPermission("symbolic"));
+        }
+
+        // get attributes of target file (don't follow links)
+        try {
+            targetAttrs = UnixFileAttributes.get(target, false);
+        } catch (UnixException x) {
+            // ignore
+        }
+        boolean targetExists = (targetAttrs != null);
+
+        // if the target exists:
+        // 1. check if source and target are the same file
+        // 2. throw exception if REPLACE_EXISTING option is not set
+        // 3. try to unlink the target
+        if (targetExists) {
+            if (sourceAttrs.isSameFile(targetAttrs))
+                return;  // nothing to do as files are identical
+            if (!flags.replaceExisting)
+                throw new FileAlreadyExistsException(
+                    target.getPathForExecptionMessage());
+            try {
+                if (targetAttrs.isDirectory()) {
+                    rmdir(target);
+                } else {
+                    unlink(target);
+                }
+            } catch (UnixException x) {
+                // target is non-empty directory that can't be replaced.
+                if (targetAttrs.isDirectory() &&
+                   (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+                {
+                    throw new FileAlreadyExistsException(
+                        source.getPathForExecptionMessage(),
+                        target.getPathForExecptionMessage(),
+                        x.getMessage());
+                }
+                x.rethrowAsIOException(target);
+            }
+        }
+
+        // do the copy
+        if (sourceAttrs.isDirectory()) {
+            copyDirectory(source, sourceAttrs, target, flags);
+            return;
+        }
+        if (sourceAttrs.isSymbolicLink()) {
+            copyLink(source, sourceAttrs, target, flags);
+            return;
+        }
+        if (!flags.interruptible) {
+            // non-interruptible file copy
+            copyFile(source, sourceAttrs, target, flags, 0L);
+            return;
+        }
+
+        // interruptible file copy
+        final UnixFileAttributes attrsToCopy = sourceAttrs;
+        Cancellable copyTask = new Cancellable() {
+            @Override public void implRun() throws IOException {
+                copyFile(source, attrsToCopy, target, flags,
+                    addressToPollForCancel());
+            }
+        };
+        try {
+            Cancellable.runInterruptibly(copyTask);
+        } catch (ExecutionException e) {
+            Throwable t = e.getCause();
+            if (t instanceof IOException)
+                throw (IOException)t;
+            throw new IOException(t);
+        }
+    }
+
+    // -- native methods --
+
+    static native void transfer(int dst, int src, long addressToPollForCancel)
+        throws UnixException;
+
+    static {
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                System.loadLibrary("nio");
+                return null;
+            }});
+    }
+
+}