Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the FileOptions.DeleteOnClose Windows specific?

I'm trying to create a temporary file using a FileStream along with FileOptions.DeleteOnClose. I'm seeing the expected behaviour when the stream is being closed, however if an exception is thrown then the file is not being deleted. I can't use using because the closing of the stream is being handled by a FileStreamResult. I know on Windows it is being handled by the winapi flag FILE_FLAG_DELETE_ON_CLOSE, but I don't know if/how this works for other platforms on .NET Core applications.

I've tried wrapping a try-catch around this to manually dispose the stream in erroneous situations, which does work but SHOULD BE unnecessary since the file would get removed anyways as soon as all handles are released.

            const FileOptions fileOptions =
                FileOptions.Asynchronous |
                FileOptions.DeleteOnClose |
                FileOptions.Encrypted |
                FileOptions.SequentialScan;

            var fileStream = new FileStream(
                Path.GetRandomFileName(),
                FileMode.Create,
                FileAccess.ReadWrite,
                FileShare.None,
                4096,
                fileOptions);

I expected the file to be deleted when all handles are released if FileOptions.DeleteOnClose is cross-platform, but the file is still present.

like image 522
Timmeh Avatar asked Jul 24 '19 06:07

Timmeh


Video Answer


1 Answers

Reading the source code of src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs, it seems FileOptions.DeleteOnClose is honoured and emulated on *nix platforms at time of disposal:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// ...

// If DeleteOnClose was requested when constructed, delete the file now.
// (Unix doesn't directly support DeleteOnClose, so we mimic it here.)
if (_path != null && (_options & FileOptions.DeleteOnClose) != 0)
{
    // Since we still have the file open, this will end up deleting
    // it (assuming we're the only link to it) once it's closed, but the
    // name will be removed immediately.
    Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist
}

The above snippet is from lines 261-269 of src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs and part of FileStream.Dispose implementation.

I created a Gist with the following test code:

using System.IO;

var fileName = Path.GetRandomFileName();
Console.WriteLine($"File name: {fileName}");
Console.WriteLine($"Exists? {File.Exists(fileName)}");
using (var fs = File.Create(fileName, 4096, FileOptions.DeleteOnClose))
    Console.WriteLine($"Exists? {File.Exists(fileName)}");
Console.WriteLine($"Exists? {File.Exists(fileName)}");

and then tested on Linux with .NET Core SDK 3.1 using Docker as follows:

> docker run --rm -it mcr.microsoft.com/dotnet/core/sdk:3.1
> root@86a890d817b7:/# dotnet tool install dotnet-script --version 0.53.0 --tool-path /usr/bin
You can invoke the tool using the following command: dotnet-script
Tool 'dotnet-script' (version '0.53.0') was successfully installed.
> root@86a890d817b7:/# dotnet script https://gist.github.com/atifaziz/2a0291a076c77e197d849eab7c049b1b/raw/9b5a08c0ffd45e09eac19325445b9fbdf799e10c/test.csx
File name: hrylnqis.nxv
Exists? False
Exists? True
Exists? False

The output seems to confirm FileOptions.DeleteOnClose is working as expected and so, no, it's not Windows-specific per your question.

Bear in mind, though, since DeleteOnClose is emulated for (some definition of) Unix, it doesn't have the same strong guarantees as on Windows. For example, if the process crashes or exits brutally via Environment.Exit then the file will be deleted on Windows but not on a Unix-based system.

like image 145
Atif Aziz Avatar answered Oct 14 '22 14:10

Atif Aziz