Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are null characters in java.io.File valid for exists check?

When a null is encountered in a filename in java.io.File, that character and all characters after it are ignored, resulting in some strange behavior in File.exists().

Is this behavior some aspect of java.io.File.exists() that I've missed?

Example:

package os;

import java.io.File;
import java.io.IOException;

public class FileNullCheck
{
    public static void main(String[] args)
    {
        File tmp = new File("a.txt");
        try
        {
            tmp.createNewFile();
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return;
        }

        String a = "a.txt";
        System.out.printf("a.txt exists: %b (len=%d)%n",new File(a).exists(),a.length());

        String anull = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00 });
        System.out.printf("a.txt (null) exists: %b (len=%d)%n",new File(anull).exists(),anull.length());

        String anullx = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00, 'x' });
        System.out.printf("a.txt (nullx) exists: %b (len=%d)%n",new File(anullx).exists(),anullx.length());
    }
}

The results of running this.

a.txt exists: true (len=5)
a.txt (null) exists: true (len=6)
a.txt (nullx) exists: true (len=7)

The Linux system has the following JVM.

Java(TM) SE Runtime Environment (build 1.7.0_10-b18)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)

The behavior seems to be C-like and the string used to validate the file on the filesystem is truncated at the null.

But I would expect the behavior at Java to return false for File.exists() on these invalid filenames.

Update: Sep 19, 2013

Java 1.7.0 update 40 has fixed this as part of bug JDK-8014846 : File and other classes in java.io do not handle embedded nulls properly

like image 474
Joakim Erdfelt Avatar asked Jan 02 '13 18:01

Joakim Erdfelt


2 Answers

On RHEL it appears that the nul byte terminates the file name (as you might expect in C)

System.out.println("a exists " + new File("a").exists());
FileOutputStream fos = new FileOutputStream(new File("a\u0000aa"));
fos.close();
System.out.println("a exists " + new File("a").exists());

prints

a exists false
a exists true

I suspect Java should prevent you from attempting to use a file name with a nul byte.

like image 179
Peter Lawrey Avatar answered Sep 30 '22 08:09

Peter Lawrey


If using JDK 1.7+ the java.nio.files.Paths.get(URI) can be used to test for Nul (it seems)

A modification to the original tests produces a useful Exception

package os;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileNullCheck
{
    public static void main(String[] args) throws Exception
    {
        File tmp = new File("a.txt");
        try
        {
            tmp.createNewFile();
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return;
        }

        String a = "a.txt";
        testExists("a.txt", a);

        String anull = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00 }, "UTF-8");
        testExists("a.txt (null)", anull);

        String anullx = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00, 'x' }, "UTF-8");
        testExists("a.txt (nullx)", anullx);
    }

    private static void testExists(String label, String filename) throws IOException
    {
        File file = new File(filename);
        System.out.printf("%s exists: %b%n", label, file.exists());
        System.out.printf("  filename.length = %d%n", filename.length());
        Path path = Paths.get(file.toURI());
        boolean symlink = Files.isSymbolicLink(path);
        System.out.printf("  nio issymlink = %b%n",symlink);
    }
}

Results in the output

a.txt exists: true
  filename.length = 5
  nio issymlink = false
a.txt (null) exists: true
  filename.length = 6
Exception in thread "main" java.nio.file.InvalidPathException: Nul character not allowed: /home/joakim/code/Stackoverflow/a.txt
    at sun.nio.fs.UnixPath.checkNotNul(UnixPath.java:93)
    at sun.nio.fs.UnixPath.normalizeAndCheck(UnixPath.java:83)
    at sun.nio.fs.UnixPath.(UnixPath.java:71)
    at sun.nio.fs.UnixFileSystem.getPath(UnixFileSystem.java:281)
    at java.io.File.toPath(File.java:2069)
    at sun.nio.fs.UnixUriUtils.fromUri(UnixUriUtils.java:61)
    at sun.nio.fs.UnixFileSystemProvider.getPath(UnixFileSystemProvider.java:97)
    at java.nio.file.Paths.get(Paths.java:138)
    at os.FileNullCheck.testExists(FileNullCheck.java:39)
    at os.FileNullCheck.main(FileNullCheck.java:28)

like image 28
Joakim Erdfelt Avatar answered Sep 30 '22 08:09

Joakim Erdfelt