Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File.Exists returns true after File.Delete

Tags:

c#

file-io

I have the following method to delete a file with a provided path

private void DestroyFile(string path)
{
    try
    {
        if (File.Exists(path))
        {
            File.Delete(path);
        }
        if (File.Exists(path))
        {
            throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

I am getting the IOException that is thrown if the file exists after the File.Delete method. Specifically

System.IO.IOException): Failed to delete file: 'C:\Windows\TEMP\[FILE NAME]'.

I have also confirmed that the file does not exist at the location in the path variable after the execution is complete. I am wondering if I am running up against a race condition between the file system updating after File.Delete and checking against it again with File.Exists. Is there a better way to smoothly delete? I know that File.Delete won't return an error if the file doesn't exist so maybe these checks are a bit redundant. Should I check if the file is in use rather than if it exists at all?

Some important additional information: The program can and does run successfully often but this particular error has been frequently seen recently.

like image 950
PeskyToaster Avatar asked May 11 '18 15:05

PeskyToaster


2 Answers

File.Delete will mark file for deletion. File really will be deleted only when all handles to it are closed (if there are no such handles - it will always be deleted after File.Delete returns). As documented for DeleteFile winapi function (which is used by C# File.Delete):

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

Usually there are no open handles to files you delete. Or, if there are open handles - they usually don't have "delete" share (this share allows another process to mark file for deletion), so when you try to delete such file - it either gets deleted (no open handles) or access denied or similar exception is thrown (some handles, but without delete share).

However, sometimes some software, such as antivirus or search indexer, might open arbitrary files with "delete" share and hold them for some time. If you try to delete such file - it will go without errors and file really will be deleted when that software closes its handle. However, File.Exists will return true for such "pending delete" file.

You can reproduce this issue with this simple program:

public class Program {
    public static void Main() {
        string path = @"G:\tmp\so\tmp.file";
        // create file with delete share and don't close handle
        var file = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete);
        DestroyFile(path);
        GC.KeepAlive(file);
    }

    private static void DestroyFile(string path) {
        try {

            if (File.Exists(path)) {
                // no error
                File.Delete(path);
            }
            // but still exists
            if (File.Exists(path)) {
                throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
            }
        }
        catch (Exception ex) {
            throw ex;
        }
    }
}

You can retry File.Exists check forever in the program above - file will exist until you close the handle.

So that's what happens in your case - some program has open handle to this file with FileShare.Delete.

You should expect such situation. For example - just remove that File.Exists check, since you marked file for deletion and it will be deleted anyway.

like image 96
Evk Avatar answered Oct 07 '22 16:10

Evk


while its not documented in the API, File.Delete WILL return before the file is completely deleted.

This is why you are running into the case you are having. Delete call will check for all the things that would make the delete fail (existing handle, lock, permission ect) and it will return after the initiation of Delete request

So its relatively safe to just put a while loop right after to wait until the file is gone or use a FileSystemWatcher to watch for Deleted event

like image 33
Steve Avatar answered Oct 07 '22 18:10

Steve