简体   繁体   中英

Did the behaviour of deleted files open with FileShare.Delete change on Windows?

We have been using the following code for several years.

    /// <summary>
    /// Opens a file and returns an exclusive handle. The file is deleted as soon as the handle is released.
    /// </summary>
    /// <param name="path">The name of the file to create</param>
    /// <returns>A FileStream backed by an exclusive handle</returns>
    /// <remarks>If another process attempts to open this file, they will recieve an UnauthorizedAccessException</remarks>
    public static System.IO.FileStream OpenAsLock(string path)
    {
        var stream = TranslateIOExceptions(() => System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.Delete));
        System.IO.File.Delete(path);
        return stream;
    }

From memory, this code used to leave a file in place until the FileStream was closed. The technique was used as part of a cooperative concurrency lock.

I found a number of other questions which make me think the behavior used to be as the comment describes: the file stays in place until the returned filestream is closed.

Will we ever be able to delete an open file in Windows?

Can using FileShare.Delete cause a UnauthorizedAccessException?

However, as part of an investigation, I've discovered Windows does not behave this way. Instead, the file is deleted as soon as the call to File.Delete is made. I also tried to reproduce the error Hans suggested would occur in the above link without success.

class Program
{
    static void Main(string[] args)
    {
        File.Open("test", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Delete);
        File.Delete("test");
        File.WriteAllText("test", "hello world");
        Console.Write(File.ReadAllText("test"));
        Console.ReadLine();
    }
}

Unfortunately, the unit test we had which might have caught this change in behavior was not configured correctly to run nightly in our environment, so I can't be certain if it ever ran green.

Was this a real change in behavior? Do we know when it happened? Was it intentional (documented)?

Many thanks to Eryk for the tip.

It turned out we did have several unit tests in place that would have caught this change in behavior, including tests that explicitly test for this behavior. I suspect those were added while investigating this strange behavior in the first place.

The unit tests had not raised the alarm yet because our test machine was running an older build of Windows 10 than my dev machine.

  • Build 17134.1304 definitely has the old behavior.
  • Build 18363.657 definitely has the new behavior.

I reviewed the list of build releases and, unfortunately, there were over two dozen releases between these two versions. However, I am very suspicious of this "Improvement and fix" listed as part of build 17763.832, available Oct 15, 2019

Addresses an issue in which files that are stored in a Cluster Shared Volume (CSV) with an alternate data stream are still present after you try to delete them. You may also receive an "access is denied" message on the next try to access or delete the files.

I'm not sure why a change specific to CSV affects my system, but the description matches exactly the change I am seeing.


In regards to the specific code, it turned out the return "FileStream" was never used in our code. Instead, we relied on the IDisposable interface, closing the stream when the "critical section" was complete, and unlocking the shared file.

Technically a breaking change, I now do the following:

  1. Create the lock file with an exclusive handle
  2. Return a new object that implements IDisposable
  3. Wait until the disposable object is disposed, then close the stream and try to delete the file
// ...
    public static IDisposable OpenAsLock(string path)
    {
        var stream = TranslateIOExceptions(() => System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None));
        return new FileBasedLock(stream, path);
    }
// ...

internal class FileBasedLock : IDisposable
{
    public FileBasedLock(FileStream stream, string path)
    {
        Stream = stream ?? throw new System.ArgumentNullException(nameof(stream));
        Path = path ?? throw new System.ArgumentNullException(nameof(path));
    }

    public FileStream Stream { get; }
    public string Path { get; }
    public void Dispose()
    {
        Stream.Close();
        try { File.Delete(Path); }
        catch (IOException) { }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM