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