893N/A/*
3909N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
893N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
893N/A *
893N/A * This code is free software; you can redistribute it and/or modify it
893N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
893N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
893N/A *
893N/A * This code is distributed in the hope that it will be useful, but WITHOUT
893N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
893N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
893N/A * version 2 for more details (a copy is included in the LICENSE file that
893N/A * accompanied this code).
893N/A *
893N/A * You should have received a copy of the GNU General Public License version
893N/A * 2 along with this work; if not, write to the Free Software Foundation,
893N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
893N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
893N/A */
893N/A
893N/Apackage sun.nio.fs;
893N/A
893N/Aimport java.nio.file.*;
893N/Aimport java.io.IOException;
893N/Aimport java.util.concurrent.ExecutionException;
893N/Aimport com.sun.nio.file.ExtendedCopyOption;
893N/A
893N/Aimport static sun.nio.fs.WindowsNativeDispatcher.*;
893N/Aimport static sun.nio.fs.WindowsConstants.*;
893N/A
893N/A/**
893N/A * Utility methods for copying and moving files.
893N/A */
893N/A
893N/Aclass WindowsFileCopy {
893N/A private WindowsFileCopy() {
893N/A }
893N/A
893N/A /**
893N/A * Copy file from source to target
893N/A */
893N/A static void copy(final WindowsPath source,
893N/A final WindowsPath target,
893N/A CopyOption... options)
893N/A throws IOException
893N/A {
893N/A // map options
893N/A boolean replaceExisting = false;
893N/A boolean copyAttributes = false;
893N/A boolean followLinks = true;
893N/A boolean interruptible = false;
893N/A for (CopyOption option: options) {
893N/A if (option == StandardCopyOption.REPLACE_EXISTING) {
893N/A replaceExisting = true;
893N/A continue;
893N/A }
893N/A if (option == LinkOption.NOFOLLOW_LINKS) {
893N/A followLinks = false;
893N/A continue;
893N/A }
893N/A if (option == StandardCopyOption.COPY_ATTRIBUTES) {
893N/A copyAttributes = true;
893N/A continue;
893N/A }
893N/A if (option == ExtendedCopyOption.INTERRUPTIBLE) {
893N/A interruptible = true;
893N/A continue;
893N/A }
893N/A if (option == null)
893N/A throw new NullPointerException();
893N/A throw new UnsupportedOperationException("Unsupported copy option");
893N/A }
893N/A
893N/A // check permissions. If the source file is a symbolic link then
893N/A // later we must also check LinkPermission
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A source.checkRead();
893N/A target.checkWrite();
893N/A }
893N/A
893N/A // get attributes of source file
893N/A // attempt to get attributes of target file
893N/A // if both files are the same there is nothing to do
893N/A // if target exists and !replace then throw exception
893N/A
893N/A WindowsFileAttributes sourceAttrs = null;
893N/A WindowsFileAttributes targetAttrs = null;
893N/A
893N/A long sourceHandle = 0L;
893N/A try {
893N/A sourceHandle = source.openForReadAttributeAccess(followLinks);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source);
893N/A }
893N/A try {
893N/A // source attributes
893N/A try {
893N/A sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source);
893N/A }
893N/A
893N/A // open target (don't follow links)
893N/A long targetHandle = 0L;
893N/A try {
893N/A targetHandle = target.openForReadAttributeAccess(false);
893N/A try {
893N/A targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
893N/A
893N/A // if both files are the same then nothing to do
893N/A if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
893N/A return;
893N/A }
893N/A
893N/A // can't replace file
893N/A if (!replaceExisting) {
893N/A throw new FileAlreadyExistsException(
893N/A target.getPathForExceptionMessage());
893N/A }
893N/A
893N/A } finally {
893N/A CloseHandle(targetHandle);
893N/A }
893N/A } catch (WindowsException x) {
893N/A // ignore
893N/A }
893N/A
893N/A } finally {
893N/A CloseHandle(sourceHandle);
893N/A }
893N/A
893N/A // if source file is a symbolic link then we must check for LinkPermission
893N/A if (sm != null && sourceAttrs.isSymbolicLink()) {
893N/A sm.checkPermission(new LinkPermission("symbolic"));
893N/A }
893N/A
893N/A final String sourcePath = asWin32Path(source);
893N/A final String targetPath = asWin32Path(target);
893N/A
893N/A // if target exists then delete it.
893N/A if (targetAttrs != null) {
893N/A try {
893N/A if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
893N/A RemoveDirectory(targetPath);
893N/A } else {
893N/A DeleteFile(targetPath);
893N/A }
893N/A } catch (WindowsException x) {
893N/A if (targetAttrs.isDirectory()) {
893N/A // ERROR_ALREADY_EXISTS is returned when attempting to delete
893N/A // non-empty directory on SAMBA servers.
893N/A if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
893N/A x.lastError() == ERROR_ALREADY_EXISTS)
893N/A {
3471N/A throw new DirectoryNotEmptyException(
893N/A target.getPathForExceptionMessage());
893N/A }
893N/A }
893N/A x.rethrowAsIOException(target);
893N/A }
893N/A }
893N/A
893N/A // Use CopyFileEx if the file is not a directory or junction
893N/A if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
893N/A final int flags =
893N/A (source.getFileSystem().supportsLinks() && !followLinks) ?
893N/A COPY_FILE_COPY_SYMLINK : 0;
893N/A
893N/A if (interruptible) {
893N/A // interruptible copy
893N/A Cancellable copyTask = new Cancellable() {
893N/A @Override
893N/A public int cancelValue() {
893N/A return 1; // TRUE
893N/A }
893N/A @Override
893N/A public void implRun() throws IOException {
893N/A try {
893N/A CopyFileEx(sourcePath, targetPath, flags,
893N/A addressToPollForCancel());
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source, target);
893N/A }
893N/A }
893N/A };
893N/A try {
893N/A Cancellable.runInterruptibly(copyTask);
893N/A } catch (ExecutionException e) {
893N/A Throwable t = e.getCause();
893N/A if (t instanceof IOException)
893N/A throw (IOException)t;
893N/A throw new IOException(t);
893N/A }
893N/A } else {
893N/A // non-interruptible copy
893N/A try {
893N/A CopyFileEx(sourcePath, targetPath, flags, 0L);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source, target);
893N/A }
893N/A }
893N/A if (copyAttributes) {
893N/A // CopyFileEx does not copy security attributes
893N/A try {
893N/A copySecurityAttributes(source, target, followLinks);
893N/A } catch (IOException x) {
893N/A // ignore
893N/A }
893N/A }
893N/A return;
893N/A }
893N/A
893N/A // copy directory or directory junction
893N/A try {
893N/A if (sourceAttrs.isDirectory()) {
893N/A CreateDirectory(targetPath, 0L);
893N/A } else {
893N/A String linkTarget = WindowsLinkSupport.readLink(source);
893N/A int flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
893N/A CreateSymbolicLink(targetPath,
6140N/A WindowsPath.addPrefixIfNeeded(linkTarget),
893N/A flags);
893N/A }
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(target);
893N/A }
893N/A if (copyAttributes) {
893N/A // copy DOS/timestamps attributes
893N/A WindowsFileAttributeViews.Dos view =
893N/A WindowsFileAttributeViews.createDosView(target, false);
893N/A try {
893N/A view.setAttributes(sourceAttrs);
893N/A } catch (IOException x) {
893N/A if (sourceAttrs.isDirectory()) {
893N/A try {
893N/A RemoveDirectory(targetPath);
893N/A } catch (WindowsException ignore) { }
893N/A }
893N/A }
893N/A
893N/A // copy security attributes. If this fail it doesn't cause the move
893N/A // to fail.
893N/A try {
893N/A copySecurityAttributes(source, target, followLinks);
893N/A } catch (IOException ignore) { }
893N/A }
893N/A }
893N/A
893N/A /**
893N/A * Move file from source to target
893N/A */
893N/A static void move(WindowsPath source, WindowsPath target, CopyOption... options)
893N/A throws IOException
893N/A {
893N/A // map options
893N/A boolean atomicMove = false;
893N/A boolean replaceExisting = false;
893N/A for (CopyOption option: options) {
893N/A if (option == StandardCopyOption.ATOMIC_MOVE) {
893N/A atomicMove = true;
893N/A continue;
893N/A }
893N/A if (option == StandardCopyOption.REPLACE_EXISTING) {
893N/A replaceExisting = true;
893N/A continue;
893N/A }
893N/A if (option == LinkOption.NOFOLLOW_LINKS) {
893N/A // ignore
893N/A continue;
893N/A }
893N/A if (option == null) throw new NullPointerException();
893N/A throw new UnsupportedOperationException("Unsupported copy option");
893N/A }
893N/A
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A source.checkWrite();
893N/A target.checkWrite();
893N/A }
893N/A
893N/A final String sourcePath = asWin32Path(source);
893N/A final String targetPath = asWin32Path(target);
893N/A
893N/A // atomic case
893N/A if (atomicMove) {
893N/A try {
893N/A MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);
893N/A } catch (WindowsException x) {
893N/A if (x.lastError() == ERROR_NOT_SAME_DEVICE) {
893N/A throw new AtomicMoveNotSupportedException(
893N/A source.getPathForExceptionMessage(),
893N/A target.getPathForExceptionMessage(),
893N/A x.errorString());
893N/A }
893N/A x.rethrowAsIOException(source, target);
893N/A }
893N/A return;
893N/A }
893N/A
893N/A // get attributes of source file
893N/A // attempt to get attributes of target file
893N/A // if both files are the same there is nothing to do
893N/A // if target exists and !replace then throw exception
893N/A
893N/A WindowsFileAttributes sourceAttrs = null;
893N/A WindowsFileAttributes targetAttrs = null;
893N/A
893N/A long sourceHandle = 0L;
893N/A try {
893N/A sourceHandle = source.openForReadAttributeAccess(false);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source);
893N/A }
893N/A try {
893N/A // source attributes
893N/A try {
893N/A sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source);
893N/A }
893N/A
893N/A // open target (don't follow links)
893N/A long targetHandle = 0L;
893N/A try {
893N/A targetHandle = target.openForReadAttributeAccess(false);
893N/A try {
893N/A targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
893N/A
893N/A // if both files are the same then nothing to do
893N/A if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
893N/A return;
893N/A }
893N/A
893N/A // can't replace file
893N/A if (!replaceExisting) {
893N/A throw new FileAlreadyExistsException(
893N/A target.getPathForExceptionMessage());
893N/A }
893N/A
893N/A } finally {
893N/A CloseHandle(targetHandle);
893N/A }
893N/A } catch (WindowsException x) {
893N/A // ignore
893N/A }
893N/A
893N/A } finally {
893N/A CloseHandle(sourceHandle);
893N/A }
893N/A
893N/A // if target exists then delete it.
893N/A if (targetAttrs != null) {
893N/A try {
893N/A if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
893N/A RemoveDirectory(targetPath);
893N/A } else {
893N/A DeleteFile(targetPath);
893N/A }
893N/A } catch (WindowsException x) {
893N/A if (targetAttrs.isDirectory()) {
893N/A // ERROR_ALREADY_EXISTS is returned when attempting to delete
893N/A // non-empty directory on SAMBA servers.
893N/A if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
893N/A x.lastError() == ERROR_ALREADY_EXISTS)
893N/A {
3471N/A throw new DirectoryNotEmptyException(
893N/A target.getPathForExceptionMessage());
893N/A }
893N/A }
893N/A x.rethrowAsIOException(target);
893N/A }
893N/A }
893N/A
893N/A // first try MoveFileEx (no options). If target is on same volume then
893N/A // all attributes (including security attributes) are preserved.
893N/A try {
893N/A MoveFileEx(sourcePath, targetPath, 0);
893N/A return;
893N/A } catch (WindowsException x) {
893N/A if (x.lastError() != ERROR_NOT_SAME_DEVICE)
893N/A x.rethrowAsIOException(source, target);
893N/A }
893N/A
893N/A // target is on different volume so use MoveFileEx with copy option
893N/A if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
893N/A try {
893N/A MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(source, target);
893N/A }
893N/A // MoveFileEx does not copy security attributes when moving
893N/A // across volumes.
893N/A try {
893N/A copySecurityAttributes(source, target, false);
893N/A } catch (IOException x) {
893N/A // ignore
893N/A }
893N/A return;
893N/A }
893N/A
893N/A // moving directory or directory-link to another file system
893N/A assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink();
893N/A
893N/A // create new directory or directory junction
893N/A try {
893N/A if (sourceAttrs.isDirectory()) {
893N/A CreateDirectory(targetPath, 0L);
893N/A } else {
893N/A String linkTarget = WindowsLinkSupport.readLink(source);
893N/A CreateSymbolicLink(targetPath,
6140N/A WindowsPath.addPrefixIfNeeded(linkTarget),
893N/A SYMBOLIC_LINK_FLAG_DIRECTORY);
893N/A }
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(target);
893N/A }
893N/A
893N/A // copy timestamps/DOS attributes
893N/A WindowsFileAttributeViews.Dos view =
893N/A WindowsFileAttributeViews.createDosView(target, false);
893N/A try {
893N/A view.setAttributes(sourceAttrs);
893N/A } catch (IOException x) {
893N/A // rollback
893N/A try {
893N/A RemoveDirectory(targetPath);
893N/A } catch (WindowsException ignore) { }
893N/A throw x;
893N/A }
893N/A
893N/A // copy security attributes. If this fails it doesn't cause the move
893N/A // to fail.
893N/A try {
893N/A copySecurityAttributes(source, target, false);
893N/A } catch (IOException ignore) { }
893N/A
893N/A // delete source
893N/A try {
893N/A RemoveDirectory(sourcePath);
893N/A } catch (WindowsException x) {
893N/A // rollback
893N/A try {
893N/A RemoveDirectory(targetPath);
893N/A } catch (WindowsException ignore) { }
893N/A // ERROR_ALREADY_EXISTS is returned when attempting to delete
893N/A // non-empty directory on SAMBA servers.
893N/A if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
893N/A x.lastError() == ERROR_ALREADY_EXISTS)
893N/A {
893N/A throw new DirectoryNotEmptyException(
893N/A target.getPathForExceptionMessage());
893N/A }
893N/A x.rethrowAsIOException(source);
893N/A }
893N/A }
893N/A
893N/A
893N/A private static String asWin32Path(WindowsPath path) throws IOException {
893N/A try {
893N/A return path.getPathForWin32Calls();
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(path);
893N/A return null;
893N/A }
893N/A }
893N/A
893N/A /**
893N/A * Copy DACL/owner/group from source to target
893N/A */
893N/A private static void copySecurityAttributes(WindowsPath source,
893N/A WindowsPath target,
893N/A boolean followLinks)
893N/A throws IOException
893N/A {
893N/A String path = WindowsLinkSupport.getFinalPath(source, followLinks);
893N/A
893N/A // may need SeRestorePrivilege to set file owner
893N/A WindowsSecurity.Privilege priv =
893N/A WindowsSecurity.enablePrivilege("SeRestorePrivilege");
893N/A try {
893N/A int request = (DACL_SECURITY_INFORMATION |
893N/A OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
893N/A NativeBuffer buffer =
893N/A WindowsAclFileAttributeView.getFileSecurity(path, request);
893N/A try {
893N/A try {
893N/A SetFileSecurity(target.getPathForWin32Calls(), request,
893N/A buffer.address());
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(target);
893N/A }
893N/A } finally {
893N/A buffer.release();
893N/A }
893N/A } finally {
893N/A priv.drop();
893N/A }
893N/A }
893N/A}