Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java walkFileTree to mimic chmod -R 755 does not work

Tags:

java

I want to ensure correct permissions in a directory tree (0755 for directories and 644 for files). Here is my class:

package NRF_Utils;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import static java.nio.file.FileVisitResult.*;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.logging.Logger;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;

public class DirectoryTreeOperations {
    private static final Logger log = Logger.getLogger("spv." + DirectoryTreeOperations.class.getName());

    public static boolean setPermissions(String root, String directoryPermissions, String filePermissions) {
        boolean result = true;
        //declaring the path to delete
        final Path path = Paths.get(root);
        final Set<PosixFilePermission> dirPermissions = PosixFilePermissions.fromString(directoryPermissions);
        final Set<PosixFilePermission> filPermissions = PosixFilePermissions.fromString(filePermissions);

        try {
            Files.walkFileTree(path, new FileVisitor<Path>() {

                @Override
                public FileVisitResult preVisitDirectory(Path dir,
                        BasicFileAttributes attrs) throws IOException {
                    System.out.println("setting dir permission on " + dir);
                    Files.setPosixFilePermissions(dir, dirPermissions);                 
                    return CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file,
                        BasicFileAttributes attrs) throws IOException {
                    System.out.println("setting file permission on " + file);
                    Files.setPosixFilePermissions(file, filPermissions);
                    return CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc)
                        throws IOException {
                    log.severe("visitFileFailed failed on " + file + " : " + exc);
                    return CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir,
                        IOException exc) throws IOException {
                    return CONTINUE;
                }

            });
        } catch (IOException e) {
            log.severe("setPermissions failed " + e);
            result = false;
        }
        return result;
    }

    // TODO remove and provide a proper unit test
    public static void main(String [] arg) {
        System.out.println ("setPermissions returned: " + setPermissions("/tmp/x", "rwxr-xr-x", "rw-r--r--"));
    }
}

To perform test, I have done:

mkdir -p /tmp/x/y/z;touch /tmp/x/y/z/f;chmod 000 /tmp/x/y/z

The output is:

setting dir permission on /tmp/x
setting dir permission on /tmp/x/y
setPermissions returned: true
nov. 26, 2017 2:45:00 PM NRF_Utils.DirectoryTreeOperations$1 visitFileFailed
GRAVE: visitFileFailed failed on /tmp/x/y/z : java.nio.file.AccessDeniedException: /tmp/x/y/z

It seems java does not want to explore the directory tree because the permissions are wrong, but my aim was to explore the directory tree to fix permissions. Is the API crazy ?

like image 780
BOC Avatar asked Jan 04 '23 03:01

BOC


2 Answers

It does seem strange it would behave this way, so maybe not a crazy API, but the implementation certainly doesn't cater to the specific case of using preVisitDirectory() to make the directory accessible.

The Files.walkFileTree() doc says:

Where the file is a directory, and the directory could not be opened, then the visitFileFailed method is invoked with the I/O exception, after which, the file tree walk continues, by default, at the next sibling of the directory.

and looking at the implementation, it does actually try to open the directory before it calls visitor.preVisitDirectory() which explains the issue you are running into.

As a solution, you could grab the FileTreeWalker.java source, and make the minor adjustment in your own local version of the FileTreeWalker - it's straight forward just look for the call to visitor.preVisitDirectory() and move the open directory code to occur after that. Then change your code to:

  new FileTreeWalker(EnumSet.noneOf(FileVisitOption.class), new FileVisitor<Path>() {
     ...
  }, Integer.MAX_VALUE).walk(path);

When you run, you'll get:

setting dir permission on /tmp/x
setting dir permission on /tmp/x/y
setting dir permission on /tmp/x/y/z
setting file permission on /tmp/x/y/z/f
setPermissions returned: true

which shows your of implementation of FileVisitor is fine.

Hope that helps.

like image 132
Paul Jowett Avatar answered Jan 05 '23 18:01

Paul Jowett


When you do chmod 755 /tmp/x/y/z from the command line, it does not check the permission on z itself. As long as you have sufficient access to y, you can perform chmod on z.

OTOH, if you look at the source code of Files.walkFileTree, it actually tries to access every target file in its own visit method, to gather file attributes etc., before it calls your visitor methods. Hence the access denied error.

By the look of it, you might have to roll your own file tree walker, where you would just go ahead change the permission without trying to access the target path first.

like image 22
jingx Avatar answered Jan 05 '23 16:01

jingx