FileStream.SetLength: действительно ли содержимое потока между старой и новой длиной действительно не определено или это нули? - PullRequest
0 голосов
/ 25 октября 2018

Документация FileStream.SetLength гласит:

содержимое потока между старой и новой длиной не определено

Но это сообщение в блоге говорит:

Самый простой способ сделать это - использовать SetFilePointer для перемещения указателя на большую позицию в файле (который еще не существует),затем используйте SetEndOfFile, чтобы расширить файл до этого размера.Этому файлу назначено дисковое пространство, но NTFS фактически еще не заполняет байты нулями.Это будет делать это лениво по требованию.

В нем говорится, что NTFS лениво заполняет байты нулями.

Я думаю FileStream.SetLength вызов SetFilePointer и SetEndOfFile под капотами.

ОБНОВЛЕНИЕ: сообщение в блоге также говорит:

Бонус болтовни: документация для SetEndOfFile гласит: «Если файл расширен, содержимое файла между старым концомфайл и новый конец файла не определены. "Но я только что сказал, что он будет заполнен нулями по требованию.Кто прав?

Формальное определение функции SetEndOfFile состоит в том, что расширенный контент не определен.Однако по соображениям безопасности NTFS гарантирует, что вы никогда не увидите чужие оставшиеся данные.(Предполагая, что вы не намеренно обходите защиту с помощью SetFileValidData.)

Однако другие файловые системы могут вести себя по-разному

Итак, похоже, что в системах NTFS файлобнуление гарантировано.

END UPDATE

SetFileValidData документация гласит:

Функция SetFileValidData позволяет избежать заполнения данных нулями при записине последовательно к файлу

Кажется, говорят, что если вы не позвоните SetFileValidData, файл будет заполнен нулями.

Так что вопрос:

Действительно ли содержимое потока между старой и новой длинами действительно не определено или это нули?

Контекст: Windows 10, NTFS, Net Framework 4.72.

Я также хотел бы спросить о coreclr Linux и других файловых системах.

Если вы не позвоните SetFileValidData, в этом случае содержимое может быть ненулевым?

Я спрашиваю, потому что я развиваюсьng 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();
                    }
                }
            });
        }
    }
}
...