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