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