From 03cff5e469a8aa7cfaba7032d30a98c81f1090b3 Mon Sep 17 00:00:00 2001 From: Tal Aloni Date: Fri, 19 May 2017 13:37:11 +0300 Subject: [PATCH] IFileSystem: Added FileOptions parameter to OpenFile --- .../Adapter/NTFileSystemAdapter.cs | 88 +++++--- SMBLibrary/SMBLibrary.csproj | 1 - SMBLibrary/Utilities/PrefetchedStream.cs | 205 ------------------ .../DirectoryFileSystem.cs | 5 +- Utilities/IFileSystem/FileSystem.cs | 11 +- Utilities/IFileSystem/IFileSystem.cs | 2 +- 6 files changed, 74 insertions(+), 238 deletions(-) delete mode 100644 SMBLibrary/Utilities/PrefetchedStream.cs diff --git a/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs index 1bec3c1..7f01fda 100644 --- a/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs +++ b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs @@ -182,7 +182,7 @@ namespace SMBLibrary // Truncate the file try { - Stream temp = m_fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); + Stream temp = m_fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.None); temp.Close(); } catch (Exception ex) @@ -266,17 +266,13 @@ namespace SMBLibrary private NTStatus OpenFileStream(out Stream stream, string path, FileAccess fileAccess, ShareAccess shareAccess, CreateOptions openOptions) { stream = null; - // When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point. - // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception - // of a file copy operation (where the caller will attempt to simply copy the reparse point). - bool openReparsePoint = (openOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0; - bool disableBuffering = (openOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0; - bool buffered = (openOptions & CreateOptions.FILE_RANDOM_ACCESS) == 0 && !disableBuffering && !openReparsePoint; FileShare fileShare = NTFileStoreHelper.ToFileShare(shareAccess); + FileOptions fileOptions = ToFileOptions(openOptions); string fileShareString = fileShare.ToString().Replace(", ", "|"); + string fileOptionsString = ToFileOptionsString(fileOptions); try { - stream = m_fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare); + stream = m_fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare, fileOptions); } catch (Exception ex) { @@ -285,12 +281,7 @@ namespace SMBLibrary return status; } - Log(Severity.Information, "OpenFileStream: Opened '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShareString, buffered); - if (buffered) - { - stream = new PrefetchedStream(stream); - } - + Log(Severity.Information, "OpenFileStream: Opened '{0}', Access={1}, Share={2}, FileOptions={3}", path, fileAccess, fileShareString, fileOptionsString); return NTStatus.STATUS_SUCCESS; } @@ -303,18 +294,6 @@ namespace SMBLibrary fileHandle.Stream.Close(); } - if (fileHandle.DeleteOnClose) - { - try - { - m_fileSystem.Delete(fileHandle.Path); - Log(Severity.Verbose, "CloseFile: Deleted '{0}'.", fileHandle.Path); - } - catch - { - Log(Severity.Verbose, "CloseFile: Error deleting '{0}'.", fileHandle.Path); - } - } return NTStatus.STATUS_SUCCESS; } @@ -462,6 +441,63 @@ namespace SMBLibrary } } + private static FileOptions ToFileOptions(CreateOptions createOptions) + { + const FileOptions FILE_FLAG_OPEN_REPARSE_POINT = (FileOptions)0x00200000; + const FileOptions FILE_FLAG_NO_BUFFERING = (FileOptions)0x20000000; + FileOptions result = FileOptions.None; + if ((createOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0) + { + result |= FILE_FLAG_OPEN_REPARSE_POINT; + } + if ((createOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0) + { + result |= FILE_FLAG_NO_BUFFERING; + } + if ((createOptions & CreateOptions.FILE_RANDOM_ACCESS) > 0) + { + result |= FileOptions.RandomAccess; + } + if ((createOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0) + { + result |= FileOptions.SequentialScan; + } + if ((createOptions & CreateOptions.FILE_WRITE_THROUGH) > 0) + { + result |= FileOptions.WriteThrough; + } + if ((createOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0) + { + result |= FileOptions.DeleteOnClose; + } + + return result; + } + + private static string ToFileOptionsString(FileOptions options) + { + string result = String.Empty; + const FileOptions FILE_FLAG_OPEN_REPARSE_POINT = (FileOptions)0x00200000; + const FileOptions FILE_FLAG_NO_BUFFERING = (FileOptions)0x20000000; + if ((options & FILE_FLAG_OPEN_REPARSE_POINT) > 0) + { + result += "ReparsePoint|"; + options &= ~FILE_FLAG_OPEN_REPARSE_POINT; + } + if ((options & FILE_FLAG_NO_BUFFERING) > 0) + { + result += "NoBuffering|"; + options &= ~FILE_FLAG_NO_BUFFERING; + } + + if (result == String.Empty || options != FileOptions.None) + { + result += options.ToString().Replace(", ", "|"); + } + result = result.TrimEnd(new char[] { '|' }); + return result; + } + /// /// Will return a virtual allocation size, assuming 4096 bytes per cluster /// diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj index 631c9f6..98467e4 100644 --- a/SMBLibrary/SMBLibrary.csproj +++ b/SMBLibrary/SMBLibrary.csproj @@ -540,7 +540,6 @@ - diff --git a/SMBLibrary/Utilities/PrefetchedStream.cs b/SMBLibrary/Utilities/PrefetchedStream.cs deleted file mode 100644 index f03e381..0000000 --- a/SMBLibrary/Utilities/PrefetchedStream.cs +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright (C) 2016-2017 Tal Aloni . All rights reserved. - * - * You can redistribute this program and/or modify it under the terms of - * the GNU Lesser Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - */ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading; - -namespace Utilities -{ - public class PrefetchedStream : Stream - { - public const int CacheSize = 524288; // 512 KB - public const int ReadAheadThershold = 65536; // 64 KB - - private long m_cacheOffset; - private byte[] m_cache = new byte[0]; - - private Stream m_stream; - - public PrefetchedStream(Stream stream) - { - m_stream = stream; - if (m_stream.CanRead) - { - ScheduleReadAhead(); - } - } - - private void ScheduleReadAhead() - { - new Thread(delegate() - { - ReadAhead(); - }).Start(); - } - - private void ReadAhead() - { - lock (m_stream) - { - long position = this.Position; - bool isInCache = (position >= m_cacheOffset) && (position < m_cacheOffset + m_cache.Length); - int bytesAlreadyRead; - if (isInCache) - { - int offsetInCache = (int)(position - m_cacheOffset); - bytesAlreadyRead = m_cache.Length - offsetInCache; - byte[] oldCache = m_cache; - m_cache = new byte[CacheSize]; - Array.Copy(oldCache, offsetInCache, m_cache, 0, bytesAlreadyRead); - this.Position = position + bytesAlreadyRead; - } - else - { - bytesAlreadyRead = 0; - m_cache = new byte[CacheSize]; - } - m_cacheOffset = position; - int bytesRead = m_stream.Read(m_cache, bytesAlreadyRead, CacheSize - bytesAlreadyRead); - System.Diagnostics.Debug.Print("[{0}] {1} bytes have been read ahead from offset {2}.", DateTime.Now.ToString("HH:mm:ss:ffff"), bytesRead, position); - if (bytesAlreadyRead + bytesRead < CacheSize) - { - // EOF, we must trim the response data array - m_cache = ByteReader.ReadBytes(m_cache, 0, bytesAlreadyRead + bytesRead); - } - this.Position = position; - } - } - - public override int Read(byte[] buffer, int offset, int count) - { - int bytesCopied; - lock (m_stream) - { - long position = this.Position; - bool isInCache = (position >= m_cacheOffset) && (position < m_cacheOffset + m_cache.Length); - if (isInCache) - { - int offsetInCache = (int)(position - m_cacheOffset); - int bytesAvailableInCache = m_cache.Length - offsetInCache; - bytesCopied = Math.Min(count, bytesAvailableInCache); - Array.Copy(m_cache, offsetInCache, buffer, offset, bytesCopied); - this.Position = position + bytesCopied; - - if (bytesCopied < count) - { - int bytesMissing = count - bytesCopied; - int bytesRead = m_stream.Read(buffer, offset + bytesCopied, bytesMissing); - } - - if (offsetInCache + ReadAheadThershold >= m_cache.Length) - { - ScheduleReadAhead(); - } - } - else - { - bytesCopied = m_stream.Read(buffer, 0, count); - ScheduleReadAhead(); - } - } - return bytesCopied; - } - - public override void Write(byte[] buffer, int offset, int count) - { - lock (m_stream) - { - m_cache = new byte[0]; - m_stream.Write(buffer, offset, count); - } - } - - public override void Close() - { - lock (m_stream) - { - m_stream.Close(); - } - base.Close(); - } - - public override bool CanRead - { - get - { - return m_stream.CanRead; - } - } - - public override bool CanSeek - { - get - { - return m_stream.CanSeek; - } - } - - public override bool CanWrite - { - get - { - return m_stream.CanWrite; - } - } - - public override long Length - { - get - { - lock (m_stream) - { - return m_stream.Length; - } - } - } - - public override long Position - { - get - { - lock (m_stream) - { - return m_stream.Position; - } - } - set - { - lock (m_stream) - { - m_stream.Position = value; - } - } - } - - public override void Flush() - { - lock (m_stream) - { - m_stream.Flush(); - } - } - - public override long Seek(long offset, SeekOrigin origin) - { - lock (m_stream) - { - return m_stream.Seek(offset, origin); - } - } - - public override void SetLength(long value) - { - lock (m_stream) - { - m_stream.SetLength(value); - } - } - } -} diff --git a/SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs b/SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs index 478b362..58d8b3f 100644 --- a/SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs +++ b/SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs @@ -137,11 +137,12 @@ namespace SMBServer return result; } - public override Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share) + public override Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { ValidatePath(path); string fullPath = m_directory.FullName + path; - FileStream fileStream = File.Open(fullPath, mode, access, share); + const int DefaultBufferSize = 4096; + FileStream fileStream = new FileStream(fullPath, mode, access, share, DefaultBufferSize, options); if (!m_openHandles.ContainsKey(fullPath.ToLower())) { m_openHandles.Add(fullPath.ToLower(), fileStream.SafeFileHandle); diff --git a/Utilities/IFileSystem/FileSystem.cs b/Utilities/IFileSystem/FileSystem.cs index 4575867..c31231a 100644 --- a/Utilities/IFileSystem/FileSystem.cs +++ b/Utilities/IFileSystem/FileSystem.cs @@ -13,7 +13,7 @@ namespace Utilities public abstract void Move(string source, string destination); public abstract void Delete(string path); public abstract List ListEntriesInDirectory(string path); - public abstract Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share); + public abstract Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options); public abstract void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived); public abstract void SetDates(string path, DateTime? creationDT, DateTime? lastWriteDT, DateTime? lastAccessDT); @@ -22,6 +22,11 @@ namespace Utilities return ListEntriesInDirectory(@"\"); } + public Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share) + { + return OpenFile(path, mode, access, share, FileOptions.None); + } + public void CopyFile(string sourcePath, string destinationPath) { const int bufferLength = 1024 * 1024; @@ -41,8 +46,8 @@ namespace Utilities { destinationFile = CreateFile(destinationPath); } - Stream sourceStream = OpenFile(sourcePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - Stream destinationStream = OpenFile(destinationPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + Stream sourceStream = OpenFile(sourcePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, FileOptions.SequentialScan); + Stream destinationStream = OpenFile(destinationPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.None); while (sourceStream.Position < sourceStream.Length) { int readSize = (int)Math.Max(bufferLength, sourceStream.Length - sourceStream.Position); diff --git a/Utilities/IFileSystem/IFileSystem.cs b/Utilities/IFileSystem/IFileSystem.cs index 6f2b36f..c1f99eb 100644 --- a/Utilities/IFileSystem/IFileSystem.cs +++ b/Utilities/IFileSystem/IFileSystem.cs @@ -48,7 +48,7 @@ namespace Utilities /// /// /// - Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share); + Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options); /// ///