jdk/src/windows/classes/sun/nio/fs/WindowsFileCopy.java
changeset 2057 3acf8e5e2ca0
child 5506 202f599c92aa
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.nio.fs;
       
    27 
       
    28 import java.nio.file.*;
       
    29 import java.io.IOException;
       
    30 import java.util.concurrent.ExecutionException;
       
    31 import com.sun.nio.file.ExtendedCopyOption;
       
    32 
       
    33 import static sun.nio.fs.WindowsNativeDispatcher.*;
       
    34 import static sun.nio.fs.WindowsConstants.*;
       
    35 
       
    36 /**
       
    37  * Utility methods for copying and moving files.
       
    38  */
       
    39 
       
    40 class WindowsFileCopy {
       
    41     private WindowsFileCopy() {
       
    42     }
       
    43 
       
    44     /**
       
    45      * Copy file from source to target
       
    46      */
       
    47     static void copy(final WindowsPath source,
       
    48                      final WindowsPath target,
       
    49                      CopyOption... options)
       
    50         throws IOException
       
    51     {
       
    52         // map options
       
    53         boolean replaceExisting = false;
       
    54         boolean copyAttributes = false;
       
    55         boolean followLinks = true;
       
    56         boolean interruptible = false;
       
    57         for (CopyOption option: options) {
       
    58             if (option == StandardCopyOption.REPLACE_EXISTING) {
       
    59                 replaceExisting = true;
       
    60                 continue;
       
    61             }
       
    62             if (option == LinkOption.NOFOLLOW_LINKS) {
       
    63                 followLinks = false;
       
    64                 continue;
       
    65             }
       
    66             if (option == StandardCopyOption.COPY_ATTRIBUTES) {
       
    67                 copyAttributes = true;
       
    68                 continue;
       
    69             }
       
    70             if (option == ExtendedCopyOption.INTERRUPTIBLE) {
       
    71                 interruptible = true;
       
    72                 continue;
       
    73             }
       
    74             if (option == null)
       
    75                 throw new NullPointerException();
       
    76             throw new UnsupportedOperationException("Unsupported copy option");
       
    77         }
       
    78 
       
    79         // check permissions. If the source file is a symbolic link then
       
    80         // later we must also check LinkPermission
       
    81         SecurityManager sm = System.getSecurityManager();
       
    82         if (sm != null) {
       
    83             source.checkRead();
       
    84             target.checkWrite();
       
    85         }
       
    86 
       
    87         // get attributes of source file
       
    88         // attempt to get attributes of target file
       
    89         // if both files are the same there is nothing to do
       
    90         // if target exists and !replace then throw exception
       
    91 
       
    92         WindowsFileAttributes sourceAttrs = null;
       
    93         WindowsFileAttributes targetAttrs = null;
       
    94 
       
    95         long sourceHandle = 0L;
       
    96         try {
       
    97             sourceHandle = source.openForReadAttributeAccess(followLinks);
       
    98         } catch (WindowsException x) {
       
    99             x.rethrowAsIOException(source);
       
   100         }
       
   101         try {
       
   102             // source attributes
       
   103             try {
       
   104                 sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
       
   105             } catch (WindowsException x) {
       
   106                 x.rethrowAsIOException(source);
       
   107             }
       
   108 
       
   109             // open target (don't follow links)
       
   110             long targetHandle = 0L;
       
   111             try {
       
   112                 targetHandle = target.openForReadAttributeAccess(false);
       
   113                 try {
       
   114                     targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
       
   115 
       
   116                     // if both files are the same then nothing to do
       
   117                     if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
       
   118                         return;
       
   119                     }
       
   120 
       
   121                     // can't replace file
       
   122                     if (!replaceExisting) {
       
   123                         throw new FileAlreadyExistsException(
       
   124                             target.getPathForExceptionMessage());
       
   125                     }
       
   126 
       
   127                 } finally {
       
   128                     CloseHandle(targetHandle);
       
   129                 }
       
   130             } catch (WindowsException x) {
       
   131                 // ignore
       
   132             }
       
   133 
       
   134         } finally {
       
   135             CloseHandle(sourceHandle);
       
   136         }
       
   137 
       
   138         // if source file is a symbolic link then we must check for LinkPermission
       
   139         if (sm != null && sourceAttrs.isSymbolicLink()) {
       
   140             sm.checkPermission(new LinkPermission("symbolic"));
       
   141         }
       
   142 
       
   143         final String sourcePath = asWin32Path(source);
       
   144         final String targetPath = asWin32Path(target);
       
   145 
       
   146         // if target exists then delete it.
       
   147         if (targetAttrs != null) {
       
   148             try {
       
   149                 if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
       
   150                     RemoveDirectory(targetPath);
       
   151                 } else {
       
   152                     DeleteFile(targetPath);
       
   153                 }
       
   154             } catch (WindowsException x) {
       
   155                 if (targetAttrs.isDirectory()) {
       
   156                     // ERROR_ALREADY_EXISTS is returned when attempting to delete
       
   157                     // non-empty directory on SAMBA servers.
       
   158                     if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
       
   159                         x.lastError() == ERROR_ALREADY_EXISTS)
       
   160                     {
       
   161                         throw new FileAlreadyExistsException(
       
   162                             target.getPathForExceptionMessage());
       
   163                     }
       
   164                 }
       
   165                 x.rethrowAsIOException(target);
       
   166             }
       
   167         }
       
   168 
       
   169         // Use CopyFileEx if the file is not a directory or junction
       
   170         if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
       
   171             final int flags =
       
   172                 (source.getFileSystem().supportsLinks() && !followLinks) ?
       
   173                 COPY_FILE_COPY_SYMLINK : 0;
       
   174 
       
   175             if (interruptible) {
       
   176                 // interruptible copy
       
   177                 Cancellable copyTask = new Cancellable() {
       
   178                     @Override
       
   179                     public int cancelValue() {
       
   180                         return 1;  // TRUE
       
   181                     }
       
   182                     @Override
       
   183                     public void implRun() throws IOException {
       
   184                         try {
       
   185                             CopyFileEx(sourcePath, targetPath, flags,
       
   186                                        addressToPollForCancel());
       
   187                         } catch (WindowsException x) {
       
   188                             x.rethrowAsIOException(source, target);
       
   189                         }
       
   190                     }
       
   191                 };
       
   192                 try {
       
   193                     Cancellable.runInterruptibly(copyTask);
       
   194                 } catch (ExecutionException e) {
       
   195                     Throwable t = e.getCause();
       
   196                     if (t instanceof IOException)
       
   197                         throw (IOException)t;
       
   198                     throw new IOException(t);
       
   199                 }
       
   200             } else {
       
   201                 // non-interruptible copy
       
   202                 try {
       
   203                     CopyFileEx(sourcePath, targetPath, flags, 0L);
       
   204                 } catch (WindowsException x) {
       
   205                     x.rethrowAsIOException(source, target);
       
   206                 }
       
   207             }
       
   208             if (copyAttributes) {
       
   209                 // CopyFileEx does not copy security attributes
       
   210                 try {
       
   211                     copySecurityAttributes(source, target, followLinks);
       
   212                 } catch (IOException x) {
       
   213                     // ignore
       
   214                 }
       
   215             }
       
   216             return;
       
   217         }
       
   218 
       
   219         // copy directory or directory junction
       
   220         try {
       
   221             if (sourceAttrs.isDirectory()) {
       
   222                 CreateDirectory(targetPath, 0L);
       
   223             } else {
       
   224                 String linkTarget = WindowsLinkSupport.readLink(source);
       
   225                 int flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
       
   226                 CreateSymbolicLink(targetPath,
       
   227                                    addPrefixIfNeeded(linkTarget),
       
   228                                    flags);
       
   229             }
       
   230         } catch (WindowsException x) {
       
   231             x.rethrowAsIOException(target);
       
   232         }
       
   233         if (copyAttributes) {
       
   234             // copy DOS/timestamps attributes
       
   235             WindowsFileAttributeViews.Dos view =
       
   236                 WindowsFileAttributeViews.createDosView(target, false);
       
   237             try {
       
   238                 view.setAttributes(sourceAttrs);
       
   239             } catch (IOException x) {
       
   240                 if (sourceAttrs.isDirectory()) {
       
   241                     try {
       
   242                         RemoveDirectory(targetPath);
       
   243                     } catch (WindowsException ignore) { }
       
   244                 }
       
   245             }
       
   246 
       
   247             // copy security attributes. If this fail it doesn't cause the move
       
   248             // to fail.
       
   249             try {
       
   250                 copySecurityAttributes(source, target, followLinks);
       
   251             } catch (IOException ignore) { }
       
   252         }
       
   253     }
       
   254 
       
   255     /**
       
   256      * Move file from source to target
       
   257      */
       
   258     static void move(WindowsPath source, WindowsPath target, CopyOption... options)
       
   259         throws IOException
       
   260     {
       
   261         // map options
       
   262         boolean atomicMove = false;
       
   263         boolean replaceExisting = false;
       
   264         for (CopyOption option: options) {
       
   265             if (option == StandardCopyOption.ATOMIC_MOVE) {
       
   266                 atomicMove = true;
       
   267                 continue;
       
   268             }
       
   269             if (option == StandardCopyOption.REPLACE_EXISTING) {
       
   270                 replaceExisting = true;
       
   271                 continue;
       
   272             }
       
   273             if (option == LinkOption.NOFOLLOW_LINKS) {
       
   274                 // ignore
       
   275                 continue;
       
   276             }
       
   277             if (option == null) throw new NullPointerException();
       
   278             throw new UnsupportedOperationException("Unsupported copy option");
       
   279         }
       
   280 
       
   281         SecurityManager sm = System.getSecurityManager();
       
   282         if (sm != null) {
       
   283             source.checkWrite();
       
   284             target.checkWrite();
       
   285         }
       
   286 
       
   287         final String sourcePath = asWin32Path(source);
       
   288         final String targetPath = asWin32Path(target);
       
   289 
       
   290         // atomic case
       
   291         if (atomicMove) {
       
   292             try {
       
   293                 MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);
       
   294             } catch (WindowsException x) {
       
   295                 if (x.lastError() == ERROR_NOT_SAME_DEVICE) {
       
   296                     throw new AtomicMoveNotSupportedException(
       
   297                         source.getPathForExceptionMessage(),
       
   298                         target.getPathForExceptionMessage(),
       
   299                         x.errorString());
       
   300                 }
       
   301                 x.rethrowAsIOException(source, target);
       
   302             }
       
   303             return;
       
   304         }
       
   305 
       
   306         // get attributes of source file
       
   307         // attempt to get attributes of target file
       
   308         // if both files are the same there is nothing to do
       
   309         // if target exists and !replace then throw exception
       
   310 
       
   311         WindowsFileAttributes sourceAttrs = null;
       
   312         WindowsFileAttributes targetAttrs = null;
       
   313 
       
   314         long sourceHandle = 0L;
       
   315         try {
       
   316             sourceHandle = source.openForReadAttributeAccess(false);
       
   317         } catch (WindowsException x) {
       
   318             x.rethrowAsIOException(source);
       
   319         }
       
   320         try {
       
   321             // source attributes
       
   322             try {
       
   323                 sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
       
   324             } catch (WindowsException x) {
       
   325                 x.rethrowAsIOException(source);
       
   326             }
       
   327 
       
   328             // open target (don't follow links)
       
   329             long targetHandle = 0L;
       
   330             try {
       
   331                 targetHandle = target.openForReadAttributeAccess(false);
       
   332                 try {
       
   333                     targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
       
   334 
       
   335                     // if both files are the same then nothing to do
       
   336                     if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
       
   337                         return;
       
   338                     }
       
   339 
       
   340                     // can't replace file
       
   341                     if (!replaceExisting) {
       
   342                         throw new FileAlreadyExistsException(
       
   343                             target.getPathForExceptionMessage());
       
   344                     }
       
   345 
       
   346                 } finally {
       
   347                     CloseHandle(targetHandle);
       
   348                 }
       
   349             } catch (WindowsException x) {
       
   350                 // ignore
       
   351             }
       
   352 
       
   353         } finally {
       
   354             CloseHandle(sourceHandle);
       
   355         }
       
   356 
       
   357         // if target exists then delete it.
       
   358         if (targetAttrs != null) {
       
   359             try {
       
   360                 if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
       
   361                     RemoveDirectory(targetPath);
       
   362                 } else {
       
   363                     DeleteFile(targetPath);
       
   364                 }
       
   365             } catch (WindowsException x) {
       
   366                 if (targetAttrs.isDirectory()) {
       
   367                     // ERROR_ALREADY_EXISTS is returned when attempting to delete
       
   368                     // non-empty directory on SAMBA servers.
       
   369                     if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
       
   370                         x.lastError() == ERROR_ALREADY_EXISTS)
       
   371                     {
       
   372                         throw new FileAlreadyExistsException(
       
   373                             target.getPathForExceptionMessage());
       
   374                     }
       
   375                 }
       
   376                 x.rethrowAsIOException(target);
       
   377             }
       
   378         }
       
   379 
       
   380         // first try MoveFileEx (no options). If target is on same volume then
       
   381         // all attributes (including security attributes) are preserved.
       
   382         try {
       
   383             MoveFileEx(sourcePath, targetPath, 0);
       
   384             return;
       
   385         } catch (WindowsException x) {
       
   386             if (x.lastError() != ERROR_NOT_SAME_DEVICE)
       
   387                 x.rethrowAsIOException(source, target);
       
   388         }
       
   389 
       
   390         // target is on different volume so use MoveFileEx with copy option
       
   391         if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
       
   392             try {
       
   393                 MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED);
       
   394             } catch (WindowsException x) {
       
   395                 x.rethrowAsIOException(source, target);
       
   396             }
       
   397             // MoveFileEx does not copy security attributes when moving
       
   398             // across volumes.
       
   399             try {
       
   400                 copySecurityAttributes(source, target, false);
       
   401             } catch (IOException x) {
       
   402                 // ignore
       
   403             }
       
   404             return;
       
   405         }
       
   406 
       
   407         // moving directory or directory-link to another file system
       
   408         assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink();
       
   409 
       
   410         // create new directory or directory junction
       
   411         try {
       
   412             if (sourceAttrs.isDirectory()) {
       
   413                 CreateDirectory(targetPath, 0L);
       
   414             } else {
       
   415                 String linkTarget = WindowsLinkSupport.readLink(source);
       
   416                 CreateSymbolicLink(targetPath,
       
   417                                    addPrefixIfNeeded(linkTarget),
       
   418                                    SYMBOLIC_LINK_FLAG_DIRECTORY);
       
   419             }
       
   420         } catch (WindowsException x) {
       
   421             x.rethrowAsIOException(target);
       
   422         }
       
   423 
       
   424         // copy timestamps/DOS attributes
       
   425         WindowsFileAttributeViews.Dos view =
       
   426                 WindowsFileAttributeViews.createDosView(target, false);
       
   427         try {
       
   428             view.setAttributes(sourceAttrs);
       
   429         } catch (IOException x) {
       
   430             // rollback
       
   431             try {
       
   432                 RemoveDirectory(targetPath);
       
   433             } catch (WindowsException ignore) { }
       
   434             throw x;
       
   435         }
       
   436 
       
   437         // copy security attributes. If this fails it doesn't cause the move
       
   438         // to fail.
       
   439         try {
       
   440             copySecurityAttributes(source, target, false);
       
   441         } catch (IOException ignore) { }
       
   442 
       
   443         // delete source
       
   444         try {
       
   445             RemoveDirectory(sourcePath);
       
   446         } catch (WindowsException x) {
       
   447             // rollback
       
   448             try {
       
   449                 RemoveDirectory(targetPath);
       
   450             } catch (WindowsException ignore) { }
       
   451             // ERROR_ALREADY_EXISTS is returned when attempting to delete
       
   452             // non-empty directory on SAMBA servers.
       
   453             if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
       
   454                 x.lastError() == ERROR_ALREADY_EXISTS)
       
   455             {
       
   456                 throw new DirectoryNotEmptyException(
       
   457                     target.getPathForExceptionMessage());
       
   458             }
       
   459             x.rethrowAsIOException(source);
       
   460         }
       
   461     }
       
   462 
       
   463 
       
   464     private static String asWin32Path(WindowsPath path) throws IOException {
       
   465         try {
       
   466             return path.getPathForWin32Calls();
       
   467         } catch (WindowsException x) {
       
   468             x.rethrowAsIOException(path);
       
   469             return null;
       
   470         }
       
   471     }
       
   472 
       
   473     /**
       
   474      * Copy DACL/owner/group from source to target
       
   475      */
       
   476     private static void copySecurityAttributes(WindowsPath source,
       
   477                                                WindowsPath target,
       
   478                                                boolean followLinks)
       
   479         throws IOException
       
   480     {
       
   481         String path = WindowsLinkSupport.getFinalPath(source, followLinks);
       
   482 
       
   483         // may need SeRestorePrivilege to set file owner
       
   484         WindowsSecurity.Privilege priv =
       
   485             WindowsSecurity.enablePrivilege("SeRestorePrivilege");
       
   486         try {
       
   487             int request = (DACL_SECURITY_INFORMATION |
       
   488                 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
       
   489             NativeBuffer buffer =
       
   490                 WindowsAclFileAttributeView.getFileSecurity(path, request);
       
   491             try {
       
   492                 try {
       
   493                     SetFileSecurity(target.getPathForWin32Calls(), request,
       
   494                         buffer.address());
       
   495                 } catch (WindowsException x) {
       
   496                     x.rethrowAsIOException(target);
       
   497                 }
       
   498             } finally {
       
   499                 buffer.release();
       
   500             }
       
   501         } finally {
       
   502             priv.drop();
       
   503         }
       
   504     }
       
   505 
       
   506     /**
       
   507      * Add long path prefix to path if required
       
   508      */
       
   509     private static String addPrefixIfNeeded(String path) {
       
   510         if (path.length() > 248) {
       
   511             if (path.startsWith("\\\\")) {
       
   512                 path = "\\\\?\\UNC" + path.substring(1, path.length());
       
   513             } else {
       
   514                 path = "\\\\?\\" + path;
       
   515             }
       
   516         }
       
   517         return path;
       
   518     }
       
   519 }