JDK-6883354 : File.mkdirs() method doesn't behave well when given /../
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 7
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2009-09-18
  • Updated: 2015-01-13
Description
On Solaris or Linux, if you do:
   rm -f -r bozo_land
   mkdir -p bozo_land/abc/../def/../ghi
what do you expect? Turns out you get all the necessary directories created:
   bozo_land/abc, bozo_land/def, and bozo_land/ghi

With java's java.io.File mkdirs() method, it seems rather... what can I say, confused???

import java.io.File;
public class Bug {
   public static void main(String args[]) {
     File f = new File("bozo_land/abc/../def/../ghi");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("First try: f.mkdirs()=" + f.mkdirs());
     System.out.println("First try: f.exists()=" + f.exists());
     System.out.println("Second try: f.mkdirs()=" + f.mkdirs());
     System.out.println("Second try: f.exists()=" + f.exists());
     f = new File("bozo_land");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("f.exists()=" + f.exists());
     f = new File("bozo_land/abc");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("f.exists()=" + f.exists());
     f = new File("bozo_land/abc/..");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("f.exists()=" + f.exists());
     f = new File("bozo_land/abc/../def");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("f.exists()=" + f.exists());
     f = new File("bozo_land/abc/../def/..");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("f.exists()=" + f.exists());
     f = new File("bozo_land/abc/../def/../ghi");
     System.out.println("f = new File(" + f.getPath() + ")");
     System.out.println("f.exists()=" + f.exists());
  }
}

Most java implementations do this:

bonsai<12> rm -f -r bozo_land/

bonsai<13> java Bug
f = new File(bozo_land/abc/../def/../ghi)
First try: f.mkdirs()=true         <--- It thinks it created the path ok
First try: f.exists()=false        <--- But it says it does not exist because abc and def were not created
Second try: f.mkdirs()=false       <--- Probably because it thinks it was created already (ghi exists)
Second try: f.exists()=false
f = new File(bozo_land)
f.exists()=true
f = new File(bozo_land/abc)
f.exists()=false
f = new File(bozo_land/abc/..)
f.exists()=false
f = new File(bozo_land/abc/../def)
f.exists()=false
f = new File(bozo_land/abc/../def/..)
f.exists()=false
f = new File(bozo_land/abc/../def/../ghi)
f.exists()=false


The javadoc on mkdirs() says:
    true if and only if the directory was created, along with all necessary parent directories; false  otherwise 

Granted, abc and def are not technically parent directories. But if after a successful mkdirs(), it still does not exist, that seems bad.

Comments
I'm not sure it should be called a problem. It just behaves differently on Unix and Windows in this particular scenario. That's the simple test I run: --------------------- import java.nio.file.*; class CreateDirTest { public static void main(String[] args) throws Exception { Path dir = FileSystems.getDefault().getPath("dir1/../dir2"); Files.createDirectories(dir); } } --------------------- On Linux it creates both dir1 and dir2, on Windows it creates only dir2. The root cause is that Linux and Windows report differently whether 'dir1/..' exists. It can be seen in command line too.
12-08-2013

If there are problems with Files.createDirectories then you can create a bug for this? When this was originally evaluated then we were confident that it matched "mkdir -p". As jdk8 is in rampdown then we need to be careful about any significant changes in behavior too.
12-08-2013

Files.createDirectories() doesn't work this way on Windows either. That's because Windows reports directories denoted by such paths as existent: .\nonExistentDir\.. .\existentFile\..
12-08-2013

EVALUATION The problem here is that File.mkdirs uses getCanonicalPath. As the file does not exist, this collapses the path to $PWD/bozo_land/ghi. To fix this requires re-implementing mkdirs to work like the native "mkdir -p". Note that this issue does not exist in the new file system API. The Files.createDirectories method does the right thing and matches the behavior of mkdir -p.
18-09-2009