Path: blob/master/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java
41139 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.nio.file.*;28import java.io.IOException;29import java.util.concurrent.ExecutionException;3031import static sun.nio.fs.WindowsNativeDispatcher.*;32import static sun.nio.fs.WindowsConstants.*;3334/**35* Utility methods for copying and moving files.36*/3738class WindowsFileCopy {39private WindowsFileCopy() {40}4142/**43* Copy file from source to target44*/45static void copy(final WindowsPath source,46final WindowsPath target,47CopyOption... options)48throws IOException49{50// map options51boolean replaceExisting = false;52boolean copyAttributes = false;53boolean followLinks = true;54boolean interruptible = false;55for (CopyOption option: options) {56if (option == StandardCopyOption.REPLACE_EXISTING) {57replaceExisting = true;58continue;59}60if (option == LinkOption.NOFOLLOW_LINKS) {61followLinks = false;62continue;63}64if (option == StandardCopyOption.COPY_ATTRIBUTES) {65copyAttributes = true;66continue;67}68if (ExtendedOptions.INTERRUPTIBLE.matches(option)) {69interruptible = true;70continue;71}72if (option == null)73throw new NullPointerException();74throw new UnsupportedOperationException("Unsupported copy option");75}7677// check permissions. If the source file is a symbolic link then78// later we must also check LinkPermission79@SuppressWarnings("removal")80SecurityManager sm = System.getSecurityManager();81if (sm != null) {82source.checkRead();83target.checkWrite();84}8586// get attributes of source file87// attempt to get attributes of target file88// if both files are the same there is nothing to do89// if target exists and !replace then throw exception9091WindowsFileAttributes sourceAttrs = null;92WindowsFileAttributes targetAttrs = null;9394long sourceHandle = 0L;95try {96sourceHandle = source.openForReadAttributeAccess(followLinks);97} catch (WindowsException x) {98x.rethrowAsIOException(source);99}100try {101// source attributes102try {103sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);104} catch (WindowsException x) {105x.rethrowAsIOException(source);106}107108// open target (don't follow links)109long targetHandle = 0L;110try {111targetHandle = target.openForReadAttributeAccess(false);112try {113targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);114115// if both files are the same then nothing to do116if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {117return;118}119120// can't replace file121if (!replaceExisting) {122throw new FileAlreadyExistsException(123target.getPathForExceptionMessage());124}125126} finally {127CloseHandle(targetHandle);128}129} catch (WindowsException x) {130// ignore131}132133} finally {134CloseHandle(sourceHandle);135}136137// if source file is a symbolic link then we must check for LinkPermission138if (sm != null && sourceAttrs.isSymbolicLink()) {139sm.checkPermission(new LinkPermission("symbolic"));140}141142// if source is a Unix domain socket, we don't want to copy it for various143// reasons including consistency with Unix144if (sourceAttrs.isUnixDomainSocket()) {145throw new IOException("Can not copy socket file");146}147148final String sourcePath = asWin32Path(source);149final String targetPath = asWin32Path(target);150151// if target exists then delete it.152if (targetAttrs != null) {153try {154if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {155RemoveDirectory(targetPath);156} else {157DeleteFile(targetPath);158}159} catch (WindowsException x) {160if (targetAttrs.isDirectory()) {161// ERROR_ALREADY_EXISTS is returned when attempting to delete162// non-empty directory on SAMBA servers.163if (x.lastError() == ERROR_DIR_NOT_EMPTY ||164x.lastError() == ERROR_ALREADY_EXISTS)165{166throw new DirectoryNotEmptyException(167target.getPathForExceptionMessage());168}169}170x.rethrowAsIOException(target);171}172}173174// Use CopyFileEx if the file is not a directory or junction175if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {176final int flags = (!followLinks) ? COPY_FILE_COPY_SYMLINK : 0;177178if (interruptible) {179// interruptible copy180Cancellable copyTask = new Cancellable() {181@Override182public int cancelValue() {183return 1; // TRUE184}185@Override186public void implRun() throws IOException {187try {188CopyFileEx(sourcePath, targetPath, flags,189addressToPollForCancel());190} catch (WindowsException x) {191x.rethrowAsIOException(source, target);192}193}194};195try {196Cancellable.runInterruptibly(copyTask);197} catch (ExecutionException e) {198Throwable t = e.getCause();199if (t instanceof IOException)200throw (IOException)t;201throw new IOException(t);202}203} else {204// non-interruptible copy205try {206CopyFileEx(sourcePath, targetPath, flags, 0L);207} catch (WindowsException x) {208x.rethrowAsIOException(source, target);209}210}211if (copyAttributes) {212// CopyFileEx does not copy security attributes213try {214copySecurityAttributes(source, target, followLinks);215} catch (IOException x) {216// ignore217}218}219return;220}221222// copy directory or directory junction223try {224if (sourceAttrs.isDirectory()) {225CreateDirectory(targetPath, 0L);226} else {227String linkTarget = WindowsLinkSupport.readLink(source);228int flags = SYMBOLIC_LINK_FLAG_DIRECTORY;229CreateSymbolicLink(targetPath,230WindowsPath.addPrefixIfNeeded(linkTarget),231flags);232}233} catch (WindowsException x) {234x.rethrowAsIOException(target);235}236if (copyAttributes) {237// copy DOS/timestamps attributes238WindowsFileAttributeViews.Dos view =239WindowsFileAttributeViews.createDosView(target, false);240try {241view.setAttributes(sourceAttrs);242} catch (IOException x) {243if (sourceAttrs.isDirectory()) {244try {245RemoveDirectory(targetPath);246} catch (WindowsException ignore) { }247}248}249250// copy security attributes. If this fail it doesn't cause the move251// to fail.252try {253copySecurityAttributes(source, target, followLinks);254} catch (IOException ignore) { }255}256}257258// throw a DirectoryNotEmpty exception if not empty259static void ensureEmptyDir(WindowsPath dir) throws IOException {260try (WindowsDirectoryStream dirStream =261new WindowsDirectoryStream(dir, (e) -> true)) {262if (dirStream.iterator().hasNext()) {263throw new DirectoryNotEmptyException(264dir.getPathForExceptionMessage());265}266}267}268269/**270* Move file from source to target271*/272static void move(WindowsPath source, WindowsPath target, CopyOption... options)273throws IOException274{275// map options276boolean atomicMove = false;277boolean replaceExisting = false;278for (CopyOption option: options) {279if (option == StandardCopyOption.ATOMIC_MOVE) {280atomicMove = true;281continue;282}283if (option == StandardCopyOption.REPLACE_EXISTING) {284replaceExisting = true;285continue;286}287if (option == LinkOption.NOFOLLOW_LINKS) {288// ignore289continue;290}291if (option == null) throw new NullPointerException();292throw new UnsupportedOperationException("Unsupported copy option");293}294295@SuppressWarnings("removal")296SecurityManager sm = System.getSecurityManager();297if (sm != null) {298source.checkWrite();299target.checkWrite();300}301302final String sourcePath = asWin32Path(source);303final String targetPath = asWin32Path(target);304305// atomic case306if (atomicMove) {307try {308MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);309} catch (WindowsException x) {310if (x.lastError() == ERROR_NOT_SAME_DEVICE) {311throw new AtomicMoveNotSupportedException(312source.getPathForExceptionMessage(),313target.getPathForExceptionMessage(),314x.errorString());315}316x.rethrowAsIOException(source, target);317}318return;319}320321// get attributes of source file322// attempt to get attributes of target file323// if both files are the same there is nothing to do324// if target exists and !replace then throw exception325326WindowsFileAttributes sourceAttrs = null;327WindowsFileAttributes targetAttrs = null;328329long sourceHandle = 0L;330try {331sourceHandle = source.openForReadAttributeAccess(false);332} catch (WindowsException x) {333x.rethrowAsIOException(source);334}335try {336// source attributes337try {338sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);339} catch (WindowsException x) {340x.rethrowAsIOException(source);341}342343// open target (don't follow links)344long targetHandle = 0L;345try {346targetHandle = target.openForReadAttributeAccess(false);347try {348targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);349350// if both files are the same then nothing to do351if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {352return;353}354355// can't replace file356if (!replaceExisting) {357throw new FileAlreadyExistsException(358target.getPathForExceptionMessage());359}360361} finally {362CloseHandle(targetHandle);363}364} catch (WindowsException x) {365// ignore366}367368} finally {369CloseHandle(sourceHandle);370}371372// if target exists then delete it.373if (targetAttrs != null) {374try {375if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {376RemoveDirectory(targetPath);377} else {378DeleteFile(targetPath);379}380} catch (WindowsException x) {381if (targetAttrs.isDirectory()) {382// ERROR_ALREADY_EXISTS is returned when attempting to delete383// non-empty directory on SAMBA servers.384if (x.lastError() == ERROR_DIR_NOT_EMPTY ||385x.lastError() == ERROR_ALREADY_EXISTS)386{387throw new DirectoryNotEmptyException(388target.getPathForExceptionMessage());389}390}391x.rethrowAsIOException(target);392}393}394395// first try MoveFileEx (no options). If target is on same volume then396// all attributes (including security attributes) are preserved.397try {398MoveFileEx(sourcePath, targetPath, 0);399return;400} catch (WindowsException x) {401if (x.lastError() != ERROR_NOT_SAME_DEVICE)402x.rethrowAsIOException(source, target);403}404405// target is on different volume so use MoveFileEx with copy option406if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {407try {408MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED);409} catch (WindowsException x) {410x.rethrowAsIOException(source, target);411}412// MoveFileEx does not copy security attributes when moving413// across volumes.414try {415copySecurityAttributes(source, target, false);416} catch (IOException x) {417// ignore418}419return;420}421422// moving directory or directory-link to another file system423assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink();424425// create new directory or directory junction426try {427if (sourceAttrs.isDirectory()) {428ensureEmptyDir(source);429CreateDirectory(targetPath, 0L);430} else {431String linkTarget = WindowsLinkSupport.readLink(source);432CreateSymbolicLink(targetPath,433WindowsPath.addPrefixIfNeeded(linkTarget),434SYMBOLIC_LINK_FLAG_DIRECTORY);435}436} catch (WindowsException x) {437x.rethrowAsIOException(target);438}439440// copy timestamps/DOS attributes441WindowsFileAttributeViews.Dos view =442WindowsFileAttributeViews.createDosView(target, false);443try {444view.setAttributes(sourceAttrs);445} catch (IOException x) {446// rollback447try {448RemoveDirectory(targetPath);449} catch (WindowsException ignore) { }450throw x;451}452453// copy security attributes. If this fails it doesn't cause the move454// to fail.455try {456copySecurityAttributes(source, target, false);457} catch (IOException ignore) { }458459// delete source460try {461RemoveDirectory(sourcePath);462} catch (WindowsException x) {463// rollback464try {465RemoveDirectory(targetPath);466} catch (WindowsException ignore) { }467// ERROR_ALREADY_EXISTS is returned when attempting to delete468// non-empty directory on SAMBA servers.469if (x.lastError() == ERROR_DIR_NOT_EMPTY ||470x.lastError() == ERROR_ALREADY_EXISTS)471{472throw new DirectoryNotEmptyException(473target.getPathForExceptionMessage());474}475x.rethrowAsIOException(source);476}477}478479480private static String asWin32Path(WindowsPath path) throws IOException {481try {482return path.getPathForWin32Calls();483} catch (WindowsException x) {484x.rethrowAsIOException(path);485return null;486}487}488489/**490* Copy DACL/owner/group from source to target491*/492private static void copySecurityAttributes(WindowsPath source,493WindowsPath target,494boolean followLinks)495throws IOException496{497String path = WindowsLinkSupport.getFinalPath(source, followLinks);498499// may need SeRestorePrivilege to set file owner500WindowsSecurity.Privilege priv =501WindowsSecurity.enablePrivilege("SeRestorePrivilege");502try {503int request = (DACL_SECURITY_INFORMATION |504OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);505NativeBuffer buffer =506WindowsAclFileAttributeView.getFileSecurity(path, request);507try {508try {509SetFileSecurity(target.getPathForWin32Calls(), request,510buffer.address());511} catch (WindowsException x) {512x.rethrowAsIOException(target);513}514} finally {515buffer.release();516}517} finally {518priv.drop();519}520}521}522523524