1 /*
   2  * Copyright (c) 2008, 2012, 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.nio.file.*;
  29 import java.nio.channels.*;
  30 import java.io.FileDescriptor;
  31 import java.util.Set;
  32 
  33 import sun.nio.ch.FileChannelImpl;
  34 import sun.nio.ch.ThreadPool;
  35 import sun.nio.ch.SimpleAsynchronousFileChannelImpl;
  36 import sun.misc.SharedSecrets;
  37 import sun.misc.JavaIOFileDescriptorAccess;
  38 
  39 import static sun.nio.fs.UnixNativeDispatcher.*;
  40 import static sun.nio.fs.UnixConstants.*;
  41 
  42 /**
  43  * Factory for FileChannels and AsynchronousFileChannels
  44  */
  45 
  46 class UnixChannelFactory {
  47     private static final JavaIOFileDescriptorAccess fdAccess =
  48         SharedSecrets.getJavaIOFileDescriptorAccess();
  49 
  50     protected UnixChannelFactory() {
  51     }
  52 
  53     /**
  54      * Represents the flags from a user-supplied set of open options.
  55      */
  56     protected static class Flags {
  57         boolean read;
  58         boolean write;
  59         boolean append;
  60         boolean truncateExisting;
  61         boolean noFollowLinks;
  62         boolean create;
  63         boolean createNew;
  64         boolean deleteOnClose;
  65         boolean sync;
  66         boolean dsync;
  67 
  68         static Flags toFlags(Set<? extends OpenOption> options) {
  69             Flags flags = new Flags();
  70             for (OpenOption option: options) {
  71                 if (option instanceof StandardOpenOption) {
  72                     switch ((StandardOpenOption)option) {
  73                         case READ : flags.read = true; break;
  74                         case WRITE : flags.write = true; break;
  75                         case APPEND : flags.append = true; break;
  76                         case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
  77                         case CREATE : flags.create = true; break;
  78                         case CREATE_NEW : flags.createNew = true; break;
  79                         case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
  80                         case SPARSE : /* ignore */ break;
  81                         case SYNC : flags.sync = true; break;
  82                         case DSYNC : flags.dsync = true; break;
  83                         default: throw new UnsupportedOperationException();
  84                     }
  85                     continue;
  86                 }
  87                 if (option == LinkOption.NOFOLLOW_LINKS && supportsNoFollowLinks()) {
  88                     flags.noFollowLinks = true;
  89                     continue;
  90                 }
  91                 if (option == null)
  92                     throw new NullPointerException();
  93                throw new UnsupportedOperationException(option + " not supported");
  94             }
  95             return flags;
  96         }
  97     }
  98 
  99 
 100     /**
 101      * Constructs a file channel from an existing (open) file descriptor
 102      */
 103     static FileChannel newFileChannel(int fd, String path, boolean reading, boolean writing) {
 104         FileDescriptor fdObj = new FileDescriptor();
 105         fdAccess.set(fdObj, fd);
 106         return FileChannelImpl.open(fdObj, path, reading, writing, null);
 107     }
 108 
 109     /**
 110      * Constructs a file channel by opening a file using a dfd/path pair
 111      */
 112     static FileChannel newFileChannel(int dfd,
 113                                       UnixPath path,
 114                                       String pathForPermissionCheck,
 115                                       Set<? extends OpenOption> options,
 116                                       int mode)
 117         throws UnixException
 118     {
 119         Flags flags = Flags.toFlags(options);
 120 
 121         // default is reading; append => writing
 122         if (!flags.read && !flags.write) {
 123             if (flags.append) {
 124                 flags.write = true;
 125             } else {
 126                 flags.read = true;
 127             }
 128         }
 129 
 130         // validation
 131         if (flags.read && flags.append)
 132             throw new IllegalArgumentException("READ + APPEND not allowed");
 133         if (flags.append && flags.truncateExisting)
 134             throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
 135 
 136         FileDescriptor fdObj = open(dfd, path, pathForPermissionCheck, flags, mode);
 137         return FileChannelImpl.open(fdObj, path.toString(),
 138             flags.read, flags.write, flags.append, null);
 139     }
 140 
 141     /**
 142      * Constructs a file channel by opening the given file.
 143      */
 144     static FileChannel newFileChannel(UnixPath path,
 145                                       Set<? extends OpenOption> options,
 146                                       int mode)
 147         throws UnixException
 148     {
 149         return newFileChannel(-1, path, null, options, mode);
 150     }
 151 
 152     /**
 153      * Constructs an asynchronous file channel by opening the given file.
 154      */
 155     static AsynchronousFileChannel newAsynchronousFileChannel(UnixPath path,
 156                                                               Set<? extends OpenOption> options,
 157                                                               int mode,
 158                                                               ThreadPool pool)
 159         throws UnixException
 160     {
 161         Flags flags = Flags.toFlags(options);
 162 
 163         // default is reading
 164         if (!flags.read && !flags.write) {
 165             flags.read = true;
 166         }
 167 
 168         // validation
 169         if (flags.append)
 170             throw new UnsupportedOperationException("APPEND not allowed");
 171 
 172         // for now use simple implementation
 173         FileDescriptor fdObj = open(-1, path, null, flags, mode);
 174         return SimpleAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);
 175     }
 176 
 177     /**
 178      * Opens file based on parameters and options, returning a FileDescriptor
 179      * encapsulating the handle to the open file.
 180      */
 181     protected static FileDescriptor open(int dfd,
 182                                          UnixPath path,
 183                                          String pathForPermissionCheck,
 184                                          Flags flags,
 185                                          int mode)
 186         throws UnixException
 187     {
 188         // map to oflags
 189         int oflags;
 190         if (flags.read && flags.write) {
 191             oflags = O_RDWR;
 192         } else {
 193             oflags = (flags.write) ? O_WRONLY : O_RDONLY;
 194         }
 195         if (flags.write) {
 196             if (flags.truncateExisting)
 197                 oflags |= O_TRUNC;
 198             if (flags.append)
 199                 oflags |= O_APPEND;
 200 
 201             // create flags
 202             if (flags.createNew) {
 203                 byte[] pathForSysCall = path.asByteArray();
 204 
 205                 // throw exception if file name is "." to avoid confusing error
 206                 if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
 207                     (pathForSysCall.length == 1 ||
 208                     (pathForSysCall[pathForSysCall.length-2] == '/')))
 209                 {
 210                     throw new UnixException(EEXIST);
 211                 }
 212                 oflags |= (O_CREAT | O_EXCL);
 213             } else {
 214                 if (flags.create)
 215                     oflags |= O_CREAT;
 216             }
 217         }
 218 
 219         // follow links by default
 220         boolean followLinks = true;
 221         if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) {
 222             if (flags.deleteOnClose && !supportsNoFollowLinks()) {
 223                 try {
 224                     if (UnixFileAttributes.get(path, false).isSymbolicLink())
 225                         throw new UnixException("DELETE_ON_CLOSE specified and file is a symbolic link");
 226                 } catch (UnixException x) {
 227                     if (!flags.create || x.errno() != ENOENT)
 228                         throw x;
 229                 }
 230             }
 231             followLinks = false;
 232             oflags |= O_NOFOLLOW;
 233         }
 234 
 235         if (flags.dsync)
 236             oflags |= O_DSYNC;
 237         if (flags.sync)
 238             oflags |= O_SYNC;
 239 
 240         // permission check before we open the file
 241         SecurityManager sm = System.getSecurityManager();
 242         if (sm != null) {
 243             if (pathForPermissionCheck == null)
 244                 pathForPermissionCheck = path.getPathForPermissionCheck();
 245             if (flags.read)
 246                 sm.checkRead(pathForPermissionCheck);
 247             if (flags.write)
 248                 sm.checkWrite(pathForPermissionCheck);
 249             if (flags.deleteOnClose)
 250                 sm.checkDelete(pathForPermissionCheck);
 251         }
 252 
 253         int fd;
 254         try {
 255             if (dfd >= 0) {
 256                 fd = openat(dfd, path.asByteArray(), oflags, mode);
 257             } else {
 258                 fd = UnixNativeDispatcher.open(path, oflags, mode);
 259             }
 260         } catch (UnixException x) {
 261             // Linux error can be EISDIR or EEXIST when file exists
 262             if (flags.createNew && (x.errno() == EISDIR)) {
 263                 x.setError(EEXIST);
 264             }
 265 
 266             // handle ELOOP to avoid confusing message
 267             if (!followLinks && (x.errno() == ELOOP)) {
 268                 x = new UnixException(x.getMessage() + " (NOFOLLOW_LINKS specified)");
 269             }
 270 
 271             throw x;
 272         }
 273 
 274         // unlink file immediately if delete on close. The spec is clear that
 275         // an implementation cannot guarantee to unlink the correct file when
 276         // replaced by an attacker after it is opened.
 277         if (flags.deleteOnClose) {
 278             try {
 279                 if (dfd >= 0) {
 280                     unlinkat(dfd, path.asByteArray(), 0);
 281                 } else {
 282                     unlink(path);
 283                 }
 284             } catch (UnixException ignore) {
 285                 // best-effort
 286             }
 287         }
 288 
 289         // create java.io.FileDescriptor
 290         FileDescriptor fdObj = new FileDescriptor();
 291         fdAccess.set(fdObj, fd);
 292         return fdObj;
 293     }
 294 }