Search code examples
javalinuxubuntuniosamba

Why does StandardOpenOption.CREATE not work on SMB via Ubuntu?


I have a Windows file server which is mounted to an Ubuntu server at /mnt/DrawingsPB. The mount point is using the SMB protocol to manage files on the Windows server.

I am trying to create a file and overwrite it if it already exists:

private Path drawingsTargetDirectory = Paths.get("/mnt/DrawingsPB/Test");

public OutputStream getOutputStream() throws IOException {
    Path directory = drawingsTargetDirectory.resolve("TestDir");
    Files.createDirectories(directory);
    Path path = directory.resolve("TestFile.txt");
    return Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE);
}

This is throwing an AccessDeniedException:

Caused by: java.nio.file.AccessDeniedException: /mnt/DrawingsPB/Test/TestDir/TestFile.txt
    at sun.nio.fs.UnixException.translateToIOException(UnixException.java:84) [rt.jar:1.8.0_382]
    at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102) [rt.jar:1.8.0_382]
    at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107) [rt.jar:1.8.0_382]
    at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214) [rt.jar:1.8.0_382]
    at java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:434) [rt.jar:1.8.0_382]
    at java.nio.file.Files.newOutputStream(Files.java:216) [rt.jar:1.8.0_382]
    at com.company.router.drawing.impl.FileSystemStreamProvider.lambda$4(FileSystemStreamProvider.java:75) [classes:]
    ... 50 more

However, if I do a manual check-and-delete and change CREATE to CREATE_NEW, it does not fail:

public OutputStream getOutputStream() throws IOException {
    Path directory = drawingsTargetDirectory.resolve("TestDir");
    Files.createDirectories(directory);
    Path path = directory.resolve("TestFile.txt");
    if (Files.exists(path))
        Files.delete(path); // Works
    return Files.newOutputStream(path, StandardOpenOption.CREATE_NEW, StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE); // Works
}

Why is this happening?


Solution

  • Why is this happening?

    The two operations you are comparing are not equivalent. Although the details differ between Windows and Linux, modifying the contents of a file, including truncating it to length zero, is a different operation, requiring different permissions, than is deleting the same file:

    • In Unix, truncating an existing file requires write permission on the file, whereas deleting it from a directory requires write permission on the directory.

    • In Windows / NTFS, you need the Write Data permission on a file to modify that file's content, including truncating it, but you need either either Delete permission on the file or Delete Subfolders and Files on its directory to delete it.

    Since you are denied access to truncate the file, you must not have had write / Write Data on the file. Since you were able to delete it, you most likely had write / Delete Subfolders and Files on the directory, but it is also possible that you had Delete (but not Write Data) on the file. The distinction between these will not be reflected in the permissions shown on the Linux side, if even it does any permissions mapping at all.