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