Search code examples
javawindowsniontfs

java7: Files.walkFileTree() and "System Volume information" on windows systems


Feeding Files.walkFileTree() with a root folder (e.g. "T:/") produces the error:

 java.nio.file.AccessDeniedException: T:\System Volume Information
    at java.nio.file.Files.newDirectoryStream(Files.java:457)
    at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:300)
    at java.nio.file.FileTreeWalker.next(FileTreeWalker.java:372)
    at java.nio.file.Files.walkFileTree(Files.java:2706)
    at java.nio.file.Files.walkFileTree(Files.java:2742)

The callback preVisitDirectory() is not called:

            @Override
        public FileVisitResult preVisitDirectory( Path aFile,
                                                  BasicFileAttributes aAttrs )
            throws IOException
        {
            if ( "System Volume Information".equals( ( aFile.getFileName() ) ) )
            {
                return FileVisitResult.SKIP_SUBTREE;
            }

            return FileVisitResult.CONTINUE;
        }

Neither the apache FileUtils.deleteDirectory( new File( "T:/" ) ) can handle this situation.

I came across to write following code:

    public static void deleteDirRecursive( Path aDir ) throws IOException
{
    if ( aDir == null )
    {
        throw new IllegalArgumentException( "aDir must not be null" );
    }

    if ( Files.notExists( aDir ) )
    {
        return;
    }

    if ( aDir.isAbsolute() && aDir.getRoot().equals( aDir ) )
    {
        myLog.debug( "Given path object is a root. On windows we cannot delete 'System Volume Information' folder!" );

        // -> iterate over the entries in root and skip "System Volume information"
        Arrays.asList( aDir.toFile().listFiles( 
            new FilenameFilter() 
            {
                @Override
                public boolean accept( File aDirectory, String aName )
                {
                    return !"System Volume Information".equals( aName );
                }
            } )
        ).forEach
            ( dir -> 
                {
                    try
                    {
                        if ( dir.isDirectory() )
                        {
                            deleteDirRecursive( Paths.get( dir.getAbsolutePath() ) );
                        }
                        else
                        {
                            Files.delete( Paths.get( dir.getAbsolutePath() ) );

                        }
                    }
                    catch ( Exception e )
                    {
                        throw new IllegalArgumentException( "", e );
                    }
                }
            );

        return;
    }

    if ( !Files.isDirectory( aDir ) )
    {
        throw new IllegalArgumentException( "given aDir is not a directory" );
    }


    Files.walkFileTree( aDir, new SimpleFileVisitor<Path>()
    {
        @Override
        public FileVisitResult visitFile( Path file,
                                          BasicFileAttributes attrs )
            throws IOException
        {
            Files.delete( file );
            return FileVisitResult.CONTINUE;
        }

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

    } );
}

Notice that the biggest part of this code is only the special handling of "System Volume Information" (the whole "if ( aDir.isAbsolute() ...."). Very ugly.

Is there a more elegant solution to exclude this folder from a tree walk?


Solution

  • I know it's late, but I had this issue too; I'll document it there.

    FileVisitor has a method called FileVisitResult visitFileFailed(T file, IOException exc)

    SimpleFileVisitor implements it by re-throwing the exception.

    Just override that method in your own implementation, handling the exception the way you deem necessary.