Path: blob/master/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java
41137 views
/*1* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.fs;2627import java.nio.file.*;28import java.nio.channels.*;29import java.io.FileDescriptor;30import java.util.Set;3132import jdk.internal.access.SharedSecrets;33import jdk.internal.access.JavaIOFileDescriptorAccess;34import sun.nio.ch.FileChannelImpl;35import sun.nio.ch.ThreadPool;36import sun.nio.ch.SimpleAsynchronousFileChannelImpl;3738import static sun.nio.fs.UnixNativeDispatcher.*;39import static sun.nio.fs.UnixConstants.*;4041/**42* Factory for FileChannels and AsynchronousFileChannels43*/4445class UnixChannelFactory {46private static final JavaIOFileDescriptorAccess fdAccess =47SharedSecrets.getJavaIOFileDescriptorAccess();4849protected UnixChannelFactory() {50}5152/**53* Represents the flags from a user-supplied set of open options.54*/55protected static class Flags {56boolean read;57boolean write;58boolean append;59boolean truncateExisting;60boolean noFollowLinks;61boolean create;62boolean createNew;63boolean deleteOnClose;64boolean sync;65boolean dsync;66boolean direct;6768static Flags toFlags(Set<? extends OpenOption> options) {69Flags flags = new Flags();70for (OpenOption option: options) {71if (option instanceof StandardOpenOption) {72switch ((StandardOpenOption)option) {73case READ : flags.read = true; break;74case WRITE : flags.write = true; break;75case APPEND : flags.append = true; break;76case TRUNCATE_EXISTING : flags.truncateExisting = true; break;77case CREATE : flags.create = true; break;78case CREATE_NEW : flags.createNew = true; break;79case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;80case SPARSE : /* ignore */ break;81case SYNC : flags.sync = true; break;82case DSYNC : flags.dsync = true; break;83default: throw new UnsupportedOperationException();84}85continue;86}87if (option == LinkOption.NOFOLLOW_LINKS && O_NOFOLLOW != 0) {88flags.noFollowLinks = true;89continue;90}9192if (ExtendedOptions.DIRECT.matches(option)) {93flags.direct = true;94continue;95}9697if (option == null)98throw new NullPointerException();99throw new UnsupportedOperationException(option + " not supported");100}101return flags;102}103}104105/**106* Constructs a file channel by opening a file using a dfd/path pair107*/108static FileChannel newFileChannel(int dfd,109UnixPath path,110String pathForPermissionCheck,111Set<? extends OpenOption> options,112int mode)113throws UnixException114{115Flags flags = Flags.toFlags(options);116117// default is reading; append => writing118if (!flags.read && !flags.write) {119if (flags.append) {120flags.write = true;121} else {122flags.read = true;123}124}125126// validation127if (flags.read && flags.append)128throw new IllegalArgumentException("READ + APPEND not allowed");129if (flags.append && flags.truncateExisting)130throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");131132FileDescriptor fdObj = open(dfd, path, pathForPermissionCheck, flags, mode);133return FileChannelImpl.open(fdObj, path.toString(), flags.read,134flags.write, flags.direct, null);135}136137/**138* Constructs a file channel by opening the given file.139*/140static FileChannel newFileChannel(UnixPath path,141Set<? extends OpenOption> options,142int mode)143throws UnixException144{145return newFileChannel(-1, path, null, options, mode);146}147148/**149* Constructs an asynchronous file channel by opening the given file.150*/151static AsynchronousFileChannel newAsynchronousFileChannel(UnixPath path,152Set<? extends OpenOption> options,153int mode,154ThreadPool pool)155throws UnixException156{157Flags flags = Flags.toFlags(options);158159// default is reading160if (!flags.read && !flags.write) {161flags.read = true;162}163164// validation165if (flags.append)166throw new UnsupportedOperationException("APPEND not allowed");167168// for now use simple implementation169FileDescriptor fdObj = open(-1, path, null, flags, mode);170return SimpleAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);171}172173/**174* Opens file based on parameters and options, returning a FileDescriptor175* encapsulating the handle to the open file.176*/177protected static FileDescriptor open(int dfd,178UnixPath path,179String pathForPermissionCheck,180Flags flags,181int mode)182throws UnixException183{184// map to oflags185int oflags;186if (flags.read && flags.write) {187oflags = O_RDWR;188} else {189oflags = (flags.write) ? O_WRONLY : O_RDONLY;190}191if (flags.write) {192if (flags.truncateExisting)193oflags |= O_TRUNC;194if (flags.append)195oflags |= O_APPEND;196197// create flags198if (flags.createNew) {199byte[] pathForSysCall = path.asByteArray();200201// throw exception if file name is "." to avoid confusing error202if ((pathForSysCall[pathForSysCall.length-1] == '.') &&203(pathForSysCall.length == 1 ||204(pathForSysCall[pathForSysCall.length-2] == '/')))205{206throw new UnixException(EEXIST);207}208oflags |= (O_CREAT | O_EXCL);209} else {210if (flags.create)211oflags |= O_CREAT;212}213}214215// follow links by default216boolean followLinks = true;217if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) {218if (flags.deleteOnClose && O_NOFOLLOW == 0) {219try {220if (UnixFileAttributes.get(path, false).isSymbolicLink())221throw new UnixException("DELETE_ON_CLOSE specified and file is a symbolic link");222} catch (UnixException x) {223if (!flags.create || x.errno() != ENOENT)224throw x;225}226}227followLinks = false;228oflags |= O_NOFOLLOW;229}230231if (flags.dsync)232oflags |= O_DSYNC;233if (flags.sync)234oflags |= O_SYNC;235if (flags.direct)236oflags |= O_DIRECT;237238// permission check before we open the file239@SuppressWarnings("removal")240SecurityManager sm = System.getSecurityManager();241if (sm != null) {242if (pathForPermissionCheck == null)243pathForPermissionCheck = path.getPathForPermissionCheck();244if (flags.read)245sm.checkRead(pathForPermissionCheck);246if (flags.write)247sm.checkWrite(pathForPermissionCheck);248if (flags.deleteOnClose)249sm.checkDelete(pathForPermissionCheck);250}251252int fd;253try {254if (dfd >= 0) {255fd = openat(dfd, path.asByteArray(), oflags, mode);256} else {257fd = UnixNativeDispatcher.open(path, oflags, mode);258}259} catch (UnixException x) {260// Linux error can be EISDIR or EEXIST when file exists261if (flags.createNew && (x.errno() == EISDIR)) {262x.setError(EEXIST);263}264265// handle ELOOP to avoid confusing message266if (!followLinks && (x.errno() == ELOOP)) {267x = new UnixException(x.getMessage() + " (NOFOLLOW_LINKS specified)");268}269270throw x;271}272273// unlink file immediately if delete on close. The spec is clear that274// an implementation cannot guarantee to unlink the correct file when275// replaced by an attacker after it is opened.276if (flags.deleteOnClose) {277try {278if (dfd >= 0) {279unlinkat(dfd, path.asByteArray(), 0);280} else {281unlink(path);282}283} catch (UnixException ignore) {284// best-effort285}286}287288// create java.io.FileDescriptor289FileDescriptor fdObj = new FileDescriptor();290fdAccess.set(fdObj, fd);291fdAccess.setAppend(fdObj, flags.append);292return fdObj;293}294}295296297