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