Home | History | Annotate | Download | only in fs
      1 /*
      2  * Copyright (c) 2008, 2013, 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 && O_NOFOLLOW != 0) {
     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(), flags.read, flags.write, flags.append, null);
    138     }
    139 
    140     /**
    141      * Constructs a file channel by opening the given file.
    142      */
    143     static FileChannel newFileChannel(UnixPath path,
    144                                       Set<? extends OpenOption> options,
    145                                       int mode)
    146         throws UnixException
    147     {
    148         return newFileChannel(-1, path, null, options, mode);
    149     }
    150 
    151     /**
    152      * Constructs an asynchronous file channel by opening the given file.
    153      */
    154     static AsynchronousFileChannel newAsynchronousFileChannel(UnixPath path,
    155                                                               Set<? extends OpenOption> options,
    156                                                               int mode,
    157                                                               ThreadPool pool)
    158         throws UnixException
    159     {
    160         Flags flags = Flags.toFlags(options);
    161 
    162         // default is reading
    163         if (!flags.read && !flags.write) {
    164             flags.read = true;
    165         }
    166 
    167         // validation
    168         if (flags.append)
    169             throw new UnsupportedOperationException("APPEND not allowed");
    170 
    171         // for now use simple implementation
    172         FileDescriptor fdObj = open(-1, path, null, flags, mode);
    173         return SimpleAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);
    174     }
    175 
    176     /**
    177      * Opens file based on parameters and options, returning a FileDescriptor
    178      * encapsulating the handle to the open file.
    179      */
    180     protected static FileDescriptor open(int dfd,
    181                                          UnixPath path,
    182                                          String pathForPermissionCheck,
    183                                          Flags flags,
    184                                          int mode)
    185         throws UnixException
    186     {
    187         // map to oflags
    188         int oflags;
    189         if (flags.read && flags.write) {
    190             oflags = O_RDWR;
    191         } else {
    192             oflags = (flags.write) ? O_WRONLY : O_RDONLY;
    193         }
    194         if (flags.write) {
    195             if (flags.truncateExisting)
    196                 oflags |= O_TRUNC;
    197             if (flags.append)
    198                 oflags |= O_APPEND;
    199 
    200             // create flags
    201             if (flags.createNew) {
    202                 byte[] pathForSysCall = path.asByteArray();
    203 
    204                 // throw exception if file name is "." to avoid confusing error
    205                 if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
    206                     (pathForSysCall.length == 1 ||
    207                     (pathForSysCall[pathForSysCall.length-2] == '/')))
    208                 {
    209                     throw new UnixException(EEXIST);
    210                 }
    211                 oflags |= (O_CREAT | O_EXCL);
    212             } else {
    213                 if (flags.create)
    214                     oflags |= O_CREAT;
    215             }
    216         }
    217 
    218         // follow links by default
    219         boolean followLinks = true;
    220         if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) {
    221             if (flags.deleteOnClose && O_NOFOLLOW == 0) {
    222                 try {
    223                     if (UnixFileAttributes.get(path, false).isSymbolicLink())
    224                         throw new UnixException("DELETE_ON_CLOSE specified and file is a symbolic link");
    225                 } catch (UnixException x) {
    226                     if (!flags.create || x.errno() != ENOENT)
    227                         throw x;
    228                 }
    229             }
    230             followLinks = false;
    231             oflags |= O_NOFOLLOW;
    232         }
    233 
    234         if (flags.dsync)
    235             oflags |= O_DSYNC;
    236         if (flags.sync)
    237             oflags |= O_SYNC;
    238 
    239         // permission check before we open the file
    240         SecurityManager sm = System.getSecurityManager();
    241         if (sm != null) {
    242             if (pathForPermissionCheck == null)
    243                 pathForPermissionCheck = path.getPathForPermissionCheck();
    244             if (flags.read)
    245                 sm.checkRead(pathForPermissionCheck);
    246             if (flags.write)
    247                 sm.checkWrite(pathForPermissionCheck);
    248             if (flags.deleteOnClose)
    249                 sm.checkDelete(pathForPermissionCheck);
    250         }
    251 
    252         int fd;
    253         try {
    254             if (dfd >= 0) {
    255                 fd = openat(dfd, path.asByteArray(), oflags, mode);
    256             } else {
    257                 fd = UnixNativeDispatcher.open(path, oflags, mode);
    258             }
    259         } catch (UnixException x) {
    260             // Linux error can be EISDIR or EEXIST when file exists
    261             if (flags.createNew && (x.errno() == EISDIR)) {
    262                 x.setError(EEXIST);
    263             }
    264 
    265             // handle ELOOP to avoid confusing message
    266             if (!followLinks && (x.errno() == ELOOP)) {
    267                 x = new UnixException(x.getMessage() + " (NOFOLLOW_LINKS specified)");
    268             }
    269 
    270             throw x;
    271         }
    272 
    273         // unlink file immediately if delete on close. The spec is clear that
    274         // an implementation cannot guarantee to unlink the correct file when
    275         // replaced by an attacker after it is opened.
    276         if (flags.deleteOnClose) {
    277             try {
    278                 if (dfd >= 0) {
    279                     unlinkat(dfd, path.asByteArray(), 0);
    280                 } else {
    281                     unlink(path);
    282                 }
    283             } catch (UnixException ignore) {
    284                 // best-effort
    285             }
    286         }
    287 
    288         // create java.io.FileDescriptor
    289         FileDescriptor fdObj = new FileDescriptor();
    290         fdAccess.set(fdObj, fd);
    291         return fdObj;
    292     }
    293 }
    294