1

I've build a simple test which creates and deletes a file (name does not change) in an infinite loop. The test does run for a couple of seconds (sometimes over 77,000 iterations!) and then fails with this exception:

Exception in thread "main" java.io.IOException: Access is denied
        at java.io.WinNTFileSystem.createFileExclusively(Native Method)
        at java.io.File.createNewFile(Unknown Source)
        at DeleteTest.main(DeleteTest.java:11)

Here's the test logic:

final File f = new File(pathname);
while (true) {
    final boolean create = f.createNewFile();
    if (!create) {
        System.out.println("crate failed");
    } else {
        final boolean delete = f.delete();
        if (!delete) {
            System.out.println("delete failed");
        }
    }
}

How is this possible? The delete call does not fail. It would tell. So delete always succeeds but createNewFile fails. This is what MSDN says about win32 api function DeleteFile:

The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.

So createNewFile does not close the file? The openjdk source tells us that the file is closed:

JNIEXPORT jboolean JNICALL
Java_java_io_Win32FileSystem_createFileExclusively(JNIEnv *env, jclass cls,
                                                   jstring pathname)
{
    jboolean rv = JNI_FALSE;
    DWORD a;

    WITH_PLATFORM_STRING(env, pathname, path) {
        int orv;
        int error;
        JVM_NativePath((char *)path);
        orv = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666);
        if (orv < 0) {
            if (orv != JVM_EEXIST) {
                error = GetLastError();

                // If a directory by the named path already exists,
                // return false (behavior of solaris and linux) instead of
                // throwing an exception
                a = GetFileAttributes(path);

                if ((a == INVALID_FILE_ATTRIBUTES) ||
                        !(a & FILE_ATTRIBUTE_DIRECTORY)) {
                    SetLastError(error);
                    JNU_ThrowIOExceptionWithLastError(env, path);
                }
            }
        } else {
            JVM_Close(orv);
            rv = JNI_TRUE;
        }
    } END_PLATFORM_STRING(env, path);
    return rv;
}

Can anyone explain this behaviour?

Eduard Wirch
  • 9,785
  • 9
  • 61
  • 73
  • http://stackoverflow.com/a/23697734/715269 – Gangnus May 16 '14 at 14:24
  • @Gangnus, I explicitly stated that the behavior was random. So: no, it's not a permission problem. – Eduard Wirch May 21 '14 at 11:09
  • I see. I am putting the answer here only because I was looking for the solution of the similar problem and yours was one of the pages I read and when I found the solution, I put a link here for helping somebody in future. It is NOT meant as the answer or an answer to your question, and it is not published as an answer, too. – Gangnus May 21 '14 at 12:59

3 Answers3

2

I've found an explanation while writing the question. I still posted the question because I wanted to share what I learned.

My application is not the only process on the system accessing files. The Windows Search Index Service for example could open this file because it wants to add it to it's index. Or the windows Explorer if it is updating the view.

Eduard Wirch
  • 9,785
  • 9
  • 61
  • 73
  • 1
    In your case I bet that it is your anti-virus software. This sort of thing happens all the time with av. – David Heffernan May 09 '12 at 13:46
  • 1
    The old Windows content indexing service had a nasty bug in this area. If you quickly created and deleted a directory, it would open a directory handle (presumably using FindFirstFile) but fail to close it, leaving a zombie directory you couldn't do anything with. The only way to kill the zombie was to restart the indexing service. – arx May 09 '12 at 19:21
1

This issue reminds me a problem I experienced recently with the File.renameTo() method. It is (was?) due to this bug in the jvm :

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6213298

A weird workaround is to call System.gc() and to retry renaming the file again (and it works...).

Not sure it has a link with your issue, but it may be worth exploring...

Yanflea
  • 3,876
  • 1
  • 14
  • 14
0

Try this:

final File f = new File("file");
    while (true) {
        final boolean create = f.createNewFile();
        if (!create) {
            System.out.println("crate failed");
        } else {
            final boolean delete = f.delete();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                System.out.println("...");
            }
            if (!delete) {
                System.out.println("delete failed");
            }
        }
    }

In this way we ensure that the file is released by the delete before invoking createNewFile.

Diego D
  • 1,735
  • 3
  • 22
  • 38
  • Well, your code does lower the chance of failure but it does not ensure success. There could be still a process which keeps the file open for longer than 10ms. – Eduard Wirch May 09 '12 at 13:16
  • Note that the order to delete the file was given to the OS, after the OS process this command no one should block it. – Diego D May 09 '12 at 13:27
  • If you want to be sure, catch the java.io.IOException in f.createNewFile() and then wait 10 ms, after that retry again. – Diego D May 09 '12 at 13:29