Files.createDirectories throws AccessDeniedException for an existing directory

Brian Burkhalter brian.burkhalter at oracle.com
Tue Nov 26 22:49:06 UTC 2024


Hi Ole,

On Nov 26, 2024, at 8:44 AM, ole.sh at gmx.de wrote:

I came a across a behavior of Files.createDirectories on Linux (RHEL8, JDK 17) that is a litte "unexpected":
The call of Files.createDirectories for an existing (and readable) directory on an NFS mount throws an AccessDeniedException. Technically this exception is correct because our process does not have the filesystem rights to create the directory, the process does have the rights to "read" the directory though. Looking at the JavaDoc of Files.createDirectories my expectation was that no exception would be thrown because the directory exists. I know that Files.createDirectories may throw an IOException and thus an AccessDeniedException but - as I said - it is a little "unexpected". What was even more suprising is that for the same directory the call to Files.createDirectories sometimes works and sometimes throws an AccessDeniedException (using the same process, user, etc. of course).

This sounds like correct behavior driven by whatever errno the mkdir system call reports. If the target is a directory, then one would not expect an exception for EEXISTS but would expect an AccessDeniedException for EACCES.

I took a closer look at it and saw that Files.createDirectories never explicitly checks for the existence of the directory but relies on the createDirectory of the underlying FileSystemProvider to throw an FileAlreadyExistsException. In case of Linux the underlying UnixFileSystemProvider calls the mkdir system call and checks/translates its return code.

I assume that you intend the system call itself here, as the JNI interface to the system call does not translate the errno value.

The problem is that the mkdir system call seems to be allowed (according to Posix?) to return any of the matching return codes in case multiple return codes are "correct”. And that happens in our NFS case.

Do you have a link to documentation of this behavior  / specification?

We verified that the mkdir call sometimes returns EACCESS and sometimes EEXIST (most likely due to some "caching" in the kernel NFS driver).

That sounds like flakiness in the system itself, not in Java, perhaps an NFS configuration issue? It seems that Java is responding as expected to the errno values encontered.

Although it is just an "edge case" I wonder if it makes sense to make Files.createDirectories more "predictable" in such cases? A simple solution might be to adapt Files.createAndCheckIsDirectory to check if the "dir" is a  directory not just in case of an FileAlreadyExistsException but also in case of an AccessDeniedException:

   private static void createAndCheckIsDirectory(Path dir,
                                                 FileAttribute<!--?-->... attrs)
       throws IOException
   {
       try {
           createDirectory(dir, attrs);
       } catch (FileAlreadyExistsException x) {
           if (!isDirectory(dir))
               throw x;
+       } catch (AccessDeniedException x) {
+           if (!isDirectory(dir))
+              throw x;

I am not sure that we want to hide the AccessDeniedException when the target is a directory. Subsequently executed code might encounter problems and it could be harder to identify the cause. Also, this code is used by all platforms, not only Linux.

       }
   }

Some additional notes:
* A similar problem was reported and fixed in JDK-8032220 for MacOS and Windows

In that issue, filtering of EISDIR was added but this appears to be an error specific to macOS.

* Though I have this problem using JDK 17 it (most likely) also could occur with the latest JDK because the implementation of Files.createDirectories seems not to have changed much.

Best regards,

Brian
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/nio-dev/attachments/20241126/3619a6e3/attachment-0001.htm>


More information about the nio-dev mailing list