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