1 /*
   2  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.nio.fs;
  27 
  28 import java.io.FileDescriptor;
  29 import java.io.IOException;
  30 import java.nio.channels.AsynchronousFileChannel;
  31 import java.nio.channels.FileChannel;
  32 import java.nio.file.LinkOption;
  33 import java.nio.file.OpenOption;
  34 import java.nio.file.StandardOpenOption;
  35 import java.util.Set;
  36 
  37 import com.sun.nio.file.ExtendedOpenOption;
  38 
  39 import sun.misc.JavaIOFileDescriptorAccess;
  40 import sun.misc.SharedSecrets;
  41 import sun.nio.ch.FileChannelImpl;
  42 import sun.nio.ch.ThreadPool;
  43 import sun.nio.ch.WindowsAsynchronousFileChannelImpl;
  44 
  45 import static sun.nio.fs.WindowsNativeDispatcher.*;
  46 import static sun.nio.fs.WindowsConstants.*;
  47 
  48 /**
  49  * Factory to create FileChannels and AsynchronousFileChannels.
  50  */
  51 
  52 class WindowsChannelFactory {
  53     private static final JavaIOFileDescriptorAccess fdAccess =
  54         SharedSecrets.getJavaIOFileDescriptorAccess();
  55 
  56     private WindowsChannelFactory() { }
  57 
  58     /**
  59      * Do not follow reparse points when opening an existing file. Do not fail
  60      * if the file is a reparse point.
  61      */
  62     static final OpenOption OPEN_REPARSE_POINT = new OpenOption() { };
  63 
  64     /**
  65      * Represents the flags from a user-supplied set of open options.
  66      */
  67     private static class Flags {
  68         boolean read;
  69         boolean write;
  70         boolean append;
  71         boolean truncateExisting;
  72         boolean create;
  73         boolean createNew;
  74         boolean deleteOnClose;
  75         boolean sparse;
  76         boolean overlapped;
  77         boolean sync;
  78         boolean dsync;
  79 
  80         // non-standard
  81         boolean shareRead = true;
  82         boolean shareWrite = true;
  83         boolean shareDelete = true;
  84         boolean noFollowLinks;
  85         boolean openReparsePoint;
  86 
  87         static Flags toFlags(Set<? extends OpenOption> options) {
  88             Flags flags = new Flags();
  89             for (OpenOption option: options) {
  90                 if (option instanceof StandardOpenOption) {
  91                     switch ((StandardOpenOption)option) {
  92                         case READ : flags.read = true; break;
  93                         case WRITE : flags.write = true; break;
  94                         case APPEND : flags.append = true; break;
  95                         case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
  96                         case CREATE : flags.create = true; break;
  97                         case CREATE_NEW : flags.createNew = true; break;
  98                         case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
  99                         case SPARSE : flags.sparse = true; break;
 100                         case SYNC : flags.sync = true; break;
 101                         case DSYNC : flags.dsync = true; break;
 102                         default: throw new UnsupportedOperationException();
 103                     }
 104                     continue;
 105                 }
 106                 if (option instanceof ExtendedOpenOption) {
 107                     switch ((ExtendedOpenOption)option) {
 108                         case NOSHARE_READ : flags.shareRead = false; break;
 109                         case NOSHARE_WRITE : flags.shareWrite = false; break;
 110                         case NOSHARE_DELETE : flags.shareDelete = false; break;
 111                         default: throw new UnsupportedOperationException();
 112                     }
 113                     continue;
 114                 }
 115                 if (option == LinkOption.NOFOLLOW_LINKS) {
 116                     flags.noFollowLinks = true;
 117                     continue;
 118                 }
 119                 if (option == OPEN_REPARSE_POINT) {
 120                     flags.openReparsePoint = true;
 121                     continue;
 122                 }
 123                 if (option == null)
 124                     throw new NullPointerException();
 125                 throw new UnsupportedOperationException();
 126             }
 127             return flags;
 128         }
 129     }
 130 
 131     /**
 132      * Open/creates file, returning FileChannel to access the file
 133      *
 134      * @param   pathForWindows
 135      *          The path of the file to open/create
 136      * @param   pathToCheck
 137      *          The path used for permission checks (if security manager)
 138      */
 139     static FileChannel newFileChannel(String pathForWindows,
 140                                       String pathToCheck,
 141                                       Set<? extends OpenOption> options,
 142                                       long pSecurityDescriptor)
 143         throws WindowsException
 144     {
 145         Flags flags = Flags.toFlags(options);
 146 
 147         // default is reading; append => writing
 148         if (!flags.read && !flags.write) {
 149             if (flags.append) {
 150                 flags.write = true;
 151             } else {
 152                 flags.read = true;
 153             }
 154         }
 155 
 156         // validation
 157         if (flags.read && flags.append)
 158             throw new IllegalArgumentException("READ + APPEND not allowed");
 159         if (flags.append && flags.truncateExisting)
 160             throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
 161 
 162         FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
 163         return FileChannelImpl.open(fdObj, pathForWindows, flags.read, flags.write, flags.append, null);
 164     }
 165 
 166     /**
 167      * Open/creates file, returning AsynchronousFileChannel to access the file
 168      *
 169      * @param   pathForWindows
 170      *          The path of the file to open/create
 171      * @param   pathToCheck
 172      *          The path used for permission checks (if security manager)
 173      * @param   pool
 174      *          The thread pool that the channel is associated with
 175      */
 176     static AsynchronousFileChannel newAsynchronousFileChannel(String pathForWindows,
 177                                                               String pathToCheck,
 178                                                               Set<? extends OpenOption> options,
 179                                                               long pSecurityDescriptor,
 180                                                               ThreadPool pool)
 181         throws IOException
 182     {
 183         Flags flags = Flags.toFlags(options);
 184 
 185         // Overlapped I/O required
 186         flags.overlapped = true;
 187 
 188         // default is reading
 189         if (!flags.read && !flags.write) {
 190             flags.read = true;
 191         }
 192 
 193         // validation
 194         if (flags.append)
 195             throw new UnsupportedOperationException("APPEND not allowed");
 196 
 197         // open file for overlapped I/O
 198         FileDescriptor fdObj;
 199         try {
 200             fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
 201         } catch (WindowsException x) {
 202             x.rethrowAsIOException(pathForWindows);
 203             return null;
 204         }
 205 
 206         // create the AsynchronousFileChannel
 207         try {
 208             return WindowsAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);
 209         } catch (IOException x) {
 210             // IOException is thrown if the file handle cannot be associated
 211             // with the completion port. All we can do is close the file.
 212             long handle = fdAccess.getHandle(fdObj);
 213             CloseHandle(handle);
 214             throw x;
 215         }
 216     }
 217 
 218     /**
 219      * Opens file based on parameters and options, returning a FileDescriptor
 220      * encapsulating the handle to the open file.
 221      */
 222     private static FileDescriptor open(String pathForWindows,
 223                                        String pathToCheck,
 224                                        Flags flags,
 225                                        long pSecurityDescriptor)
 226         throws WindowsException
 227     {
 228         // set to true if file must be truncated after open
 229         boolean truncateAfterOpen = false;
 230 
 231         // map options
 232         int dwDesiredAccess = 0;
 233         if (flags.read)
 234             dwDesiredAccess |= GENERIC_READ;
 235         if (flags.write)
 236             dwDesiredAccess |= GENERIC_WRITE;
 237 
 238         int dwShareMode = 0;
 239         if (flags.shareRead)
 240             dwShareMode |= FILE_SHARE_READ;
 241         if (flags.shareWrite)
 242             dwShareMode |= FILE_SHARE_WRITE;
 243         if (flags.shareDelete)
 244             dwShareMode |= FILE_SHARE_DELETE;
 245 
 246         int dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
 247         int dwCreationDisposition = OPEN_EXISTING;
 248         if (flags.write) {
 249             if (flags.createNew) {
 250                 dwCreationDisposition = CREATE_NEW;
 251                 // force create to fail if file is orphaned reparse point
 252                 dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
 253             } else {
 254                 if (flags.create)
 255                     dwCreationDisposition = OPEN_ALWAYS;
 256                 if (flags.truncateExisting) {
 257                     // Windows doesn't have a creation disposition that exactly
 258                     // corresponds to CREATE + TRUNCATE_EXISTING so we use
 259                     // the OPEN_ALWAYS mode and then truncate the file.
 260                     if (dwCreationDisposition == OPEN_ALWAYS) {
 261                         truncateAfterOpen = true;
 262                     } else {
 263                         dwCreationDisposition = TRUNCATE_EXISTING;
 264                     }
 265                 }
 266             }
 267         }
 268 
 269         if (flags.dsync || flags.sync)
 270             dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
 271         if (flags.overlapped)
 272             dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
 273         if (flags.deleteOnClose)
 274             dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
 275 
 276         // NOFOLLOW_LINKS and NOFOLLOW_REPARSEPOINT mean open reparse point
 277         boolean okayToFollowLinks = true;
 278         if (dwCreationDisposition != CREATE_NEW &&
 279             (flags.noFollowLinks ||
 280              flags.openReparsePoint ||
 281              flags.deleteOnClose))
 282         {
 283             if (flags.noFollowLinks || flags.deleteOnClose)
 284                 okayToFollowLinks = false;
 285             dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
 286         }
 287 
 288         // permission check
 289         if (pathToCheck != null) {
 290             SecurityManager sm = System.getSecurityManager();
 291             if (sm != null) {
 292                 if (flags.read)
 293                     sm.checkRead(pathToCheck);
 294                 if (flags.write)
 295                     sm.checkWrite(pathToCheck);
 296                 if (flags.deleteOnClose)
 297                     sm.checkDelete(pathToCheck);
 298             }
 299         }
 300 
 301         // open file
 302         long handle = CreateFile(pathForWindows,
 303                                  dwDesiredAccess,
 304                                  dwShareMode,
 305                                  pSecurityDescriptor,
 306                                  dwCreationDisposition,
 307                                  dwFlagsAndAttributes);
 308 
 309         // make sure this isn't a symbolic link.
 310         if (!okayToFollowLinks) {
 311             try {
 312                 if (WindowsFileAttributes.readAttributes(handle).isSymbolicLink())
 313                     throw new WindowsException("File is symbolic link");
 314             } catch (WindowsException x) {
 315                 CloseHandle(handle);
 316                 throw x;
 317             }
 318         }
 319 
 320         // truncate file (for CREATE + TRUNCATE_EXISTING case)
 321         if (truncateAfterOpen) {
 322             try {
 323                 SetEndOfFile(handle);
 324             } catch (WindowsException x) {
 325                 CloseHandle(handle);
 326                 throw x;
 327             }
 328         }
 329 
 330         // make the file sparse if needed
 331         if (dwCreationDisposition == CREATE_NEW && flags.sparse) {
 332             try {
 333                 DeviceIoControlSetSparse(handle);
 334             } catch (WindowsException x) {
 335                 // ignore as sparse option is hint
 336             }
 337         }
 338 
 339         // create FileDescriptor and return
 340         FileDescriptor fdObj = new FileDescriptor();
 341         fdAccess.setHandle(fdObj, handle);
 342         return fdObj;
 343     }
 344 }