FileStream.SetLength:新旧长度之间的流的内容是否真正未定义,或者为零?

时间:2018-10-25 12:33:51

标签: c# linux filesystems coreclr c#-7.3

FileStream.SetLength文档说:

  

新旧长度之间的流内容为   未定义

但是this blog post说:

  

最基本的方法是使用Set­File­Pointer移动   指向文件中较大位置的指针(不存在   ),然后使用Set­End­Of­File将文件扩展到该大小。这个   文件已分配了磁盘空间,但NTFS实际上并未填充   零字节。它将按需懒惰地执行该操作。

它说NTFS延迟地用零填充字节。

我想FileStream.SetLength在幕后叫SetFilePointerSetEndOfFile

更新: 博客文章还说:

  

奖金聊天:Set­End­Of­File的文档说:“如果   文件扩展后,文件的内容在旧端之间   文件和文件的新结尾未定义。”但是我只是说   它将按需填充为零。谁是对的?

     

Set­End­Of­File函数的正式定义是   扩展内容未定义。但是,NTFS将确保您   出于安全原因,永远都不会看到别人的剩余数据。   (假设您不是故意使用来绕过安全性   设置­文件­有效­数据。)

     

其他文件系统可能会选择不同的行为

因此,似乎在NTFS系统上可以保证文件归零。

END UPDATE

SetFileValidData文档说:

  

SetFileValidData函数使您避免使用以下命令填充数据   不连续写入文件时为零

似乎是说,如果您不调用SetFileValidData,则该文件将填充零。

所以问题是:

新旧长度之间的流的内容是否真的不确定,还是为零?

上下文:Windows 10,NTFS,Net Framework 4.72。

我还想问一下coreclr Linux和其他文件系统。

如果您不致电SetFileValidData,在这种情况下内容可能非零?

我之所以问是因为我正在开发PersistentHashing依赖零填充的开源项目。

我编写了一个寻找非零值的小程序,但从未找到一个:

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
using System.Threading.Tasks;

namespace TestZeros
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var cs = new CancellationTokenSource();
            Console.WriteLine("Searching non zeros ...");
            Console.WriteLine("Press enter to exit");
            try
            {
                var testTask = Test(cs.Token);
                var readLineTask = Task.Run(() =>
                {
                    Console.ReadLine();
                    cs.Cancel();
                });
                long nonZeroValue = await testTask;
                Console.WriteLine($"Found {nonZeroValue} in the file");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Non zero values couldn't be found");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Something went wrong: \n" + ex.ToString());
            }
        }

        static unsafe Task<long> Test(CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                while (true)
                {
                    const int OneGigabyte = 1024 * 1024 * 1024;
                    string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mmap.bin");

                    // create mmap file and accessor, then adquire pointer 
                    var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                    fileStream.SetLength(OneGigabyte);

                    var mmap = MemoryMappedFile.CreateFromFile(fileStream, null, fileStream.Length,
                        MemoryMappedFileAccess.ReadWrite,
                        null, HandleInheritability.None, true);

                    var accessor = mmap.CreateViewAccessor(0, 0, MemoryMappedFileAccess.ReadWrite);
                    byte* baseAddress = null;
                    accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref baseAddress);
                    byte* endAddress = baseAddress + OneGigabyte;
                    try
                    {
                        long* pointer = (long*)baseAddress;
                        while (pointer < endAddress)
                        {
                            if (cancellationToken.IsCancellationRequested) throw new OperationCanceledException();
                            if (*pointer != 0) return *pointer;
                            pointer++;
                        }
                        pointer = (long*)baseAddress;
                        while (pointer < endAddress)
                        {
                            if (cancellationToken.IsCancellationRequested) throw new OperationCanceledException();
                            *pointer = -1L;
                            pointer++;
                        }
                        accessor.Flush();
                    }
                    finally
                    {
                        accessor.SafeMemoryMappedViewHandle.ReleasePointer();
                        accessor.SafeMemoryMappedViewHandle.Close();
                        accessor.Dispose();
                        mmap.SafeMemoryMappedFileHandle.Close();
                        mmap.Dispose();
                        fileStream.Dispose();
                    }
                }
            });
        }
    }
}

0 个答案:

没有答案