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 
UnixChannelFactory()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 
toFlags(Set<? extends OpenOption> options)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      */
newFileChannel(int fd, String path, boolean reading, boolean writing)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      */
newFileChannel(int dfd, UnixPath path, String pathForPermissionCheck, Set<? extends OpenOption> options, int mode)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      */
newFileChannel(UnixPath path, Set<? extends OpenOption> options, int mode)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      */
newAsynchronousFileChannel(UnixPath path, Set<? extends OpenOption> options, int mode, ThreadPool pool)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      */
open(int dfd, UnixPath path, String pathForPermissionCheck, Flags flags, int mode)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