JDK-6463971 : File.renameTo() may not natively do copy-and-delete but must do rename
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-08-25
  • Updated: 2011-02-16
  • Resolved: 2006-12-11
Description
FULL PRODUCT VERSION :
java version "1.5.0_08"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_08-b03)
Java HotSpot(TM) Client VM (build 1.5.0_08-b03, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
java.io.File.renameTo()'s native implementation on Windows XP is not done by calling the OS's internal rename() command, but instead seems to be implemented like "copy(),delete()". The problem is that, when some process (e. g. NOTEPAD.exe) has a lock on that file, you will get an empty new file, while the old one still exists. Now remove the lock, try the same thing again, and it will fail, because now there is an empty file with that name already. "rename" is an atomic operation in almost any OS, and it should be atomic on Java, too.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Start "NOTEPAD.exe C:\x.txt", when it asks to create the file, say "OK". Type a few characters, then press SAVE (CTRL+S).
Start a Java program that does the following lines:
File a = new File("C:\\x.txt");
a.isRead()
a.isWrite()
a.renameTo(new  File("C:\\y.txt"));
Close NOTEPAD.exe


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
a.isRead() should return true
a.isWrite() should return false
a.renameTo() should return false
C:\\y.txt should not get created.
ACTUAL -
a.isRead() returns true (ok)
a.isWrite() returns true (bug)
a.renameTo() return false (ok)
C:\\y.txt gets created, is 0 Bytes large. (bug)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No errors got reported.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class A {
  public final static void main(final String[] args) throws IOException, 
InterruptedException {
    final File a = new File("C:\\x.txt");
    if (a.exists()) a.delete();
    a.createNewFile();
    FileOutputStream s = new FileOutputStream(a);
    s.write(123);
    s.close();
    final File b = new File("C:\\y.txt");
    if (b.exists()) b.delete();
    Runtime.getRuntime().exec("NOTEPAD.exe " + a.getPath());
    Thread.currentThread().sleep(5000);
    System.out.println(a.canRead());
    System.out.println(a.canWrite());
    System.out.println(a.renameTo(b));
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I didn't find a workaround. It seems it is impossible to get an atomic rename in Java, which is needed for a lot of UNIX legacy systems.

Comments
EVALUATION not reproducible either.
11-12-2006

EVALUATION -- The specification for File's renameTo method does not set any expectations and clearly allows for implementations that do "copy and delete". As Martin points out, the implementation in Sun's JDK does use the Windows rename function. To my knowledge, Microsoft does not document how the rename function is implemented but we assume it uses MoveFile or MoveFileEx with the "copy allowed" flag enabled. The JDK has used the rename function for many years and it cannot be changed now -- ie: changing it to use MoveFileEx with the "copy allowed" flag set to 0 is likely to break many Windows deployed applications that depend on the current behaviour that does appear a rename across devices. Finally, the submitter appears to assume that Notepad is locking the file and that this should prevent the file from being renamed. I did a few quick tests with Notepad and it does not appear to lock the file. While Notepad is opened it seems to be possible to use the DOS "move" and "erase" commands to rename or delete the file. I was unable to duplicate the zero-byte issue - is it possible that the rename occured before the user saved the file with data? It would be good to get further details on this but it's highly unlikely this is a Java SE bug.
29-08-2006

EVALUATION I agree that user expectations are that Java rename maps to the native rename, and that includes atomicity and permissions, etc. However, looking at the sources ./native/java/io/Win32FileSystem_md.c:391: if (rename(fromPath, toPath) == 0) { ./native/java/io/WinNTFileSystem_md.c:541: if (_wrename(frompath, topath) == 0) { it appears that native rename functions are indeed called. What's up with that? Was this fixed in mustang?
25-08-2006