maress
maress

Reputation: 3533

FileChannel.open(path, CREATE|CREATE_NEW) without WRITE option throws NoSuchFileException

I had the following code:


  @Nonnull
  @SneakyThrows
  private Pair<InputStream, Long> probeSize(@Nonnull final InputStream image) {
    final String tmpId = UUID.randomUUID().toString();
    final File probeFile = new File(tmpDir, tmpId + ".jpg");

    try (final FileChannel outChannel = FileChannel.open(probeFile.toPath(), CREATE);
         final ReadableByteChannel innChannel = Channels.newChannel(image)) {
      outChannel.transferFrom(innChannel, 0, Long.MAX_VALUE);
    }

    final Long fileSize = probeFile.length();
    return Pair.of(new FileInputStream(probeFile), fileSize);
  }

This code consistently threw the following exception:

Caused by: java.nio.file.NoSuchFileException: /tmp/4bbc9008-e91c-4f18-b0f2-c61eed35066e.jpg
    at sun.nio.fs.UnixException.translateToIOException(Unknown Source)
    at sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
    at sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
    at sun.nio.fs.UnixFileSystemProvider.newFileChannel(Unknown Source)

looking at the javadoc of FileChannel.open(path, option) and the associated StandardOpenOption, there is no documentation that alludes to the fact that, to create a file, you must also open it for write.

The only options that work:

I only determined this by going through the UnixChannelFactory.newFileChannel and noticed the following:

UnixChannelFactory:

protected static FileDescriptor open(int dfd,
                                         UnixPath path,
                                         String pathForPermissionCheck,
                                         Flags flags,
                                         int mode)
        throws UnixException
    {
        // map to oflags
        int oflags;
        if (flags.read && flags.write) {
            oflags = O_RDWR;
        } else {
            oflags = (flags.write) ? O_WRONLY : O_RDONLY;
        }
        if (flags.write) {
            if (flags.truncateExisting)
                oflags |= O_TRUNC;
            if (flags.append)
                oflags |= O_APPEND;

            // create flags
            if (flags.createNew) {
                byte[] pathForSysCall = path.asByteArray();

                // throw exception if file name is "." to avoid confusing error
                if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
                    (pathForSysCall.length == 1 ||
                    (pathForSysCall[pathForSysCall.length-2] == '/')))
                {
                    throw new UnixException(EEXIST);
                }
                oflags |= (O_CREAT | O_EXCL);
            } else {
                if (flags.create)
                    oflags |= O_CREAT;
            }
        }

Which shows that, unless you specify WRITE option, the file will never be created.

Is this a bug or an intended functionality, that FileChannel.open cannot create a file unless it is opened for write?

Upvotes: 0

Views: 1224

Answers (1)

AndrewF
AndrewF

Reputation: 1906

I'm looking at the JDK 7 Javadoc for FileChannel.open(...).

The doc for the method says:

The READ and WRITE options determine if the file should be opened for reading and/or writing. If neither option (or the APPEND option) is contained in the array then the file is opened for reading.

The doc for CREATE_NEW says:

This option is ignored when the file is opened only for reading.

The doc for CREATE says:

This option is ignored if the CREATE_NEW option is also present or the file is opened only for reading.

Putting these three snippets together, yes, this is expected behavior.

Upvotes: 3

Related Questions