From fb43fb966d66e3bdedab8c287a48e8176ed64e8d Mon Sep 17 00:00:00 2001 From: Tal Aloni Date: Sat, 11 Feb 2017 11:37:17 +0200 Subject: [PATCH] INTFileStore interface was added for better separation between the object store layer and the SMB layer --- SMBLibrary/NTFileStore/Adapter/FileHandle.cs | 29 ++ .../Adapter/NTFileSystemAdapter.Query.cs} | 55 ++-- .../NTFileSystemAdapter.QueryDirectory.cs | 261 ++++++++++++++++++ .../NTFileSystemAdapter.QueryFileSystem.cs} | 34 +-- .../Adapter/NTFileSystemAdapter.Set.cs} | 49 ++-- .../Adapter/NTFileSystemAdapter.cs} | 242 +++++++++------- SMBLibrary/NTFileStore/INTFileStore.cs | 37 +++ SMBLibrary/NTFileStore/NTFileStoreHelper.cs | 31 +++ SMBLibrary/NTFileStore/NamedPipeStore.cs | 150 ++++++++++ SMBLibrary/SMBLibrary.csproj | 22 +- .../Server/ConnectionState/OpenFileObject.cs | 8 +- .../Server/ConnectionState/OpenSearch.cs | 4 +- .../Server/ConnectionState/SMB1Session.cs | 16 +- .../Server/ConnectionState/SMB2Session.cs | 22 +- .../Server/Helpers/NTFileSystemHelper.Find.cs | 155 ----------- .../Server/SMB1/FileSystemResponseHelper.cs | 195 ++++--------- SMBLibrary/Server/SMB1/NTCreateHelper.cs | 122 +++----- SMBLibrary/Server/SMB1/OpenAndXHelper.cs | 67 ++--- .../Server/SMB1/ReadWriteResponseHelper.cs | 12 +- .../Server/SMB1/SMB1FileStoreHelper.Query.cs | 200 ++++++++++++++ .../SMB1FileStoreHelper.QueryDirectory.cs | 139 ++++++++++ .../SMB1FileStoreHelper.QueryFileSystem.cs | 99 +++++++ .../Server/SMB1/SMB1FileStoreHelper.Set.cs | 58 ++++ SMBLibrary/Server/SMB1/SMB1FileStoreHelper.cs | 184 ++++++++++++ .../Server/SMB1/SMB1FileSystemHelper.Find.cs | 116 -------- .../Server/SMB1/SMB1FileSystemHelper.Query.cs | 155 ----------- .../SMB1FileSystemHelper.QueryFileSystem.cs | 63 ----- .../Server/SMB1/SMB1FileSystemHelper.Set.cs | 163 ----------- .../Server/SMB1/ServerResponseHelper.cs | 18 +- .../SMB1/Transaction2SubcommandHelper.cs | 82 +++--- .../SMB1/TransactionSubcommandHelper.cs | 14 +- SMBLibrary/Server/SMB2/CloseHelper.cs | 28 +- SMBLibrary/Server/SMB2/CreateHelper.cs | 78 ++---- SMBLibrary/Server/SMB2/IOCtlHelper.cs | 14 +- .../Server/SMB2/QueryDirectoryHelper.cs | 118 +------- SMBLibrary/Server/SMB2/QueryInfoHelper.cs | 21 +- .../Server/SMB2/ReadWriteResponseHelper.cs | 4 +- SMBLibrary/Server/SMB2/SetInfoHelper.cs | 62 ++--- SMBLibrary/Server/SMBServer.SMB1.cs | 4 +- SMBLibrary/Server/SMBServer.SMB2.cs | 6 +- SMBLibrary/Server/Shares/FileSystemShare.cs | 12 +- SMBLibrary/Server/Shares/ISMBShare.cs | 5 + SMBLibrary/Server/Shares/NamedPipeShare.cs | 48 +--- 43 files changed, 1715 insertions(+), 1487 deletions(-) create mode 100644 SMBLibrary/NTFileStore/Adapter/FileHandle.cs rename SMBLibrary/{Server/Helpers/NTFileSystemHelper.Query.cs => NTFileStore/Adapter/NTFileSystemAdapter.Query.cs} (82%) create mode 100644 SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryDirectory.cs rename SMBLibrary/{Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs => NTFileStore/Adapter/NTFileSystemAdapter.QueryFileSystem.cs} (74%) rename SMBLibrary/{Server/Helpers/NTFileSystemHelper.Set.cs => NTFileStore/Adapter/NTFileSystemAdapter.Set.cs} (63%) rename SMBLibrary/{Server/Helpers/NTFileSystemHelper.cs => NTFileStore/Adapter/NTFileSystemAdapter.cs} (64%) create mode 100644 SMBLibrary/NTFileStore/INTFileStore.cs create mode 100644 SMBLibrary/NTFileStore/NamedPipeStore.cs delete mode 100644 SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs create mode 100644 SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Query.cs create mode 100644 SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryDirectory.cs create mode 100644 SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryFileSystem.cs create mode 100644 SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Set.cs create mode 100644 SMBLibrary/Server/SMB1/SMB1FileStoreHelper.cs delete mode 100644 SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Find.cs delete mode 100644 SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Query.cs delete mode 100644 SMBLibrary/Server/SMB1/SMB1FileSystemHelper.QueryFileSystem.cs delete mode 100644 SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Set.cs diff --git a/SMBLibrary/NTFileStore/Adapter/FileHandle.cs b/SMBLibrary/NTFileStore/Adapter/FileHandle.cs new file mode 100644 index 0000000..914da46 --- /dev/null +++ b/SMBLibrary/NTFileStore/Adapter/FileHandle.cs @@ -0,0 +1,29 @@ +/* Copyright (C) 2014-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; + +namespace SMBLibrary +{ + public class FileHandle + { + public string Path; + public bool IsDirectory; + public Stream Stream; + public bool DeleteOnClose; + + public FileHandle(string path, bool isDirectory, Stream stream, bool deleteOnClose) + { + Path = path; + IsDirectory = isDirectory; + Stream = stream; + DeleteOnClose = deleteOnClose; + } + } +} diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Query.cs b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.Query.cs similarity index 82% rename from SMBLibrary/Server/Helpers/NTFileSystemHelper.Query.cs rename to SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.Query.cs index 4abddd1..8561ea5 100644 --- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Query.cs +++ b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.Query.cs @@ -9,36 +9,33 @@ using System.Collections.Generic; using System.IO; using Utilities; -namespace SMBLibrary.Server +namespace SMBLibrary { - public partial class NTFileSystemHelper + public partial class NTFileSystemAdapter { - public static NTStatus GetNamedPipeInformation(out FileInformation result, FileInformationClass informationClass) + public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass) { - switch (informationClass) + FileHandle fileHandle = (FileHandle)handle; + string path = fileHandle.Path; + FileSystemEntry entry; + try { - case FileInformationClass.FileBasicInformation: - { - FileBasicInformation information = new FileBasicInformation(); - information.FileAttributes = FileAttributes.Temporary; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case FileInformationClass.FileStandardInformation: - { - FileStandardInformation information = new FileStandardInformation(); - information.DeletePending = true; - result = information; - return NTStatus.STATUS_SUCCESS; - } - default: - result = null; - return NTStatus.STATUS_INVALID_INFO_CLASS; + entry = m_fileSystem.GetEntry(path); + } + catch (Exception ex) + { + NTStatus status = ToNTStatus(ex); + Log(Severity.Debug, "GetFileInformation on '{0}' failed. {1}", path, status); + result = null; + return status; + } + + if (entry == null) + { + result = null; + return NTStatus.STATUS_NO_SUCH_FILE; } - } - public static NTStatus GetFileInformation(out FileInformation result, FileSystemEntry entry, bool deletePending, FileInformationClass informationClass) - { switch (informationClass) { case FileInformationClass.FileBasicInformation: @@ -55,10 +52,10 @@ namespace SMBLibrary.Server case FileInformationClass.FileStandardInformation: { FileStandardInformation information = new FileStandardInformation(); - information.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); + information.AllocationSize = (long)GetAllocationSize(entry.Size); information.EndOfFile = (long)entry.Size; information.Directory = entry.IsDirectory; - information.DeletePending = deletePending; + information.DeletePending = fileHandle.DeleteOnClose; result = information; return NTStatus.STATUS_SUCCESS; } @@ -118,7 +115,7 @@ namespace SMBLibrary.Server information.StandardInformation.AllocationSize = (long)GetAllocationSize(entry.Size); information.StandardInformation.EndOfFile = (long)entry.Size; information.StandardInformation.Directory = entry.IsDirectory; - information.StandardInformation.DeletePending = deletePending; + information.StandardInformation.DeletePending = fileHandle.DeleteOnClose; information.NameInformation.FileName = entry.Name; result = information; return NTStatus.STATUS_SUCCESS; @@ -134,7 +131,7 @@ namespace SMBLibrary.Server // A buffer of FileStreamInformation data elements is returned by the server. FileStreamInformation information = new FileStreamInformation(); information.StreamSize = (long)entry.Size; - information.StreamAllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); + information.StreamAllocationSize = (long)GetAllocationSize(entry.Size); information.StreamName = "::$DATA"; result = information; return NTStatus.STATUS_SUCCESS; @@ -166,7 +163,7 @@ namespace SMBLibrary.Server information.LastAccessTime = entry.LastAccessTime; information.LastWriteTime = entry.LastWriteTime; information.ChangeTime = entry.LastWriteTime; - information.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); + information.AllocationSize = (long)GetAllocationSize(entry.Size); information.EndOfFile = (long)entry.Size; information.FileAttributes = GetFileAttributes(entry); result = information; diff --git a/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryDirectory.cs b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryDirectory.cs new file mode 100644 index 0000000..62c3ac0 --- /dev/null +++ b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryDirectory.cs @@ -0,0 +1,261 @@ +/* Copyright (C) 2014-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.Text; +using Utilities; + +namespace SMBLibrary +{ + public partial class NTFileSystemAdapter + { + /// Expression as described in [MS-FSA] 2.1.4.4 + public NTStatus QueryDirectory(out List result, object handle, string fileName, FileInformationClass informationClass) + { + result = null; + FileHandle directoryHandle = (FileHandle)handle; + if (!directoryHandle.IsDirectory) + { + return NTStatus.STATUS_INVALID_PARAMETER; + } + + if (fileName == String.Empty) + { + return NTStatus.STATUS_INVALID_PARAMETER; + } + + string path = directoryHandle.Path; + bool findExactName = !ContainsWildcardCharacters(fileName); + + List entries; + if (!findExactName) + { + try + { + entries = m_fileSystem.ListEntriesInDirectory(path); + } + catch (UnauthorizedAccessException) + { + return NTStatus.STATUS_ACCESS_DENIED; + } + + entries = GetFiltered(entries, fileName); + + // Windows will return "." and ".." when enumerating directory files. + // The SMB1 / SMB2 specifications mandate that when zero entries are found, the server SHOULD / MUST return STATUS_NO_SUCH_FILE. + // For this reason, we MUST include the current directory and/or parent directory when enumerating a directory + // in order to diffrentiate between a directory that does not exist and a directory with no entries. + FileSystemEntry currentDirectory = m_fileSystem.GetEntry(path); + currentDirectory.Name = "."; + FileSystemEntry parentDirectory = m_fileSystem.GetEntry(FileSystem.GetParentDirectory(path)); + parentDirectory.Name = ".."; + entries.Insert(0, parentDirectory); + entries.Insert(0, currentDirectory); + } + else + { + path = FileSystem.GetDirectoryPath(path); + FileSystemEntry entry = m_fileSystem.GetEntry(path + fileName); + if (entry == null) + { + return NTStatus.STATUS_NO_SUCH_FILE; + } + entries = new List(); + entries.Add(entry); + } + + try + { + result = FromFileSystemEntries(entries, informationClass); + } + catch (UnsupportedInformationLevelException) + { + return NTStatus.STATUS_INVALID_INFO_CLASS; + } + return NTStatus.STATUS_SUCCESS; + } + + /// Expression as described in [MS-FSA] 2.1.4.4 + private static List GetFiltered(List entries, string expression) + { + if (expression == "*") + { + return entries; + } + + List result = new List(); + foreach (FileSystemEntry entry in entries) + { + if (IsFileNameInExpression(entry.Name, expression)) + { + result.Add(entry); + } + } + return result; + } + + private static bool ContainsWildcardCharacters(string expression) + { + return (expression.Contains("?") || expression.Contains("*") || expression.Contains("\"") || expression.Contains(">") || expression.Contains("<")); + } + + // [MS-FSA] 2.1.4.4 + // The FileName is string compared with Expression using the following wildcard rules: + // * (asterisk) Matches zero or more characters. + // ? (question mark) Matches a single character. + // DOS_DOT (" quotation mark) Matches either a period or zero characters beyond the name string. + // DOS_QM (> greater than) Matches any single character or, upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs. + // DOS_STAR (< less than) Matches zero or more characters until encountering and matching the final . in the name. + private static bool IsFileNameInExpression(string fileName, string expression) + { + if (expression == "*") + { + return true; + } + else if (expression.EndsWith("*")) // expression.Length > 1 + { + string desiredFileNameStart = expression.Substring(0, expression.Length - 1); + bool findExactNameWithoutExtension = false; + if (desiredFileNameStart.EndsWith("\"")) + { + findExactNameWithoutExtension = true; + desiredFileNameStart = desiredFileNameStart.Substring(0, desiredFileNameStart.Length - 1); + } + + if (!findExactNameWithoutExtension) + { + if (fileName.StartsWith(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + else + { + if (fileName.StartsWith(desiredFileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) || + fileName.Equals(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + } + else if (expression.StartsWith("<")) + { + string desiredFileNameEnd = expression.Substring(1); + if (fileName.EndsWith(desiredFileNameEnd, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + else if (String.Equals(fileName, expression, StringComparison.CurrentCultureIgnoreCase)) + { + return true; + } + return false; + } + + private static List FromFileSystemEntries(List entries, FileInformationClass informationClass) + { + List result = new List(); + foreach (FileSystemEntry entry in entries) + { + QueryDirectoryFileInformation information = FromFileSystemEntry(entry, informationClass); + result.Add(information); + } + return result; + } + + private static QueryDirectoryFileInformation FromFileSystemEntry(FileSystemEntry entry, FileInformationClass informationClass) + { + switch (informationClass) + { + case FileInformationClass.FileBothDirectoryInformation: + { + FileBothDirectoryInformation result = new FileBothDirectoryInformation(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.ChangeTime = entry.LastWriteTime; + result.EndOfFile = (long)entry.Size; + result.AllocationSize = (long)GetAllocationSize(entry.Size); + result.FileAttributes = GetFileAttributes(entry); + result.EaSize = 0; + result.ShortName = GetShortName(entry.Name); + result.FileName = entry.Name; + return result; + } + case FileInformationClass.FileDirectoryInformation: + { + FileDirectoryInformation result = new FileDirectoryInformation(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.ChangeTime = entry.LastWriteTime; + result.EndOfFile = (long)entry.Size; + result.AllocationSize = (long)GetAllocationSize(entry.Size); + result.FileAttributes = GetFileAttributes(entry); + result.FileName = entry.Name; + return result; + } + case FileInformationClass.FileFullDirectoryInformation: + { + FileFullDirectoryInformation result = new FileFullDirectoryInformation(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.ChangeTime = entry.LastWriteTime; + result.EndOfFile = (long)entry.Size; + result.AllocationSize = (long)GetAllocationSize(entry.Size); + result.FileAttributes = GetFileAttributes(entry); + result.EaSize = 0; + result.FileName = entry.Name; + return result; + } + case FileInformationClass.FileIdBothDirectoryInformation: + { + FileIdBothDirectoryInformation result = new FileIdBothDirectoryInformation(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.ChangeTime = entry.LastWriteTime; + result.EndOfFile = (long)entry.Size; + result.AllocationSize = (long)GetAllocationSize(entry.Size); + result.FileAttributes = GetFileAttributes(entry); + result.EaSize = 0; + result.ShortName = GetShortName(entry.Name); + result.FileId = 0; + result.FileName = entry.Name; + return result; + } + case FileInformationClass.FileIdFullDirectoryInformation: + { + FileIdFullDirectoryInformation result = new FileIdFullDirectoryInformation(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.ChangeTime = entry.LastWriteTime; + result.EndOfFile = (long)entry.Size; + result.AllocationSize = (long)GetAllocationSize(entry.Size); + result.FileAttributes = GetFileAttributes(entry); + result.EaSize = 0; + result.FileId = 0; + result.FileName = entry.Name; + return result; + } + case FileInformationClass.FileNamesInformation: + { + FileNamesInformation result = new FileNamesInformation(); + result.FileName = entry.Name; + return result; + } + default: + { + throw new UnsupportedInformationLevelException(); + } + } + } + } +} diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryFileSystem.cs similarity index 74% rename from SMBLibrary/Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs rename to SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryFileSystem.cs index b4ca2c2..d3ad866 100644 --- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs +++ b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.QueryFileSystem.cs @@ -9,11 +9,11 @@ using System.Collections.Generic; using System.IO; using Utilities; -namespace SMBLibrary.Server +namespace SMBLibrary { - public partial class NTFileSystemHelper + public partial class NTFileSystemAdapter { - public static NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass, IFileSystem fileSystem) + public NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass) { switch (informationClass) { @@ -27,10 +27,10 @@ namespace SMBLibrary.Server case FileSystemInformationClass.FileFsSizeInformation: { FileFsSizeInformation information = new FileFsSizeInformation(); - information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize; - information.AvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; - information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector; - information.BytesPerSector = NTFileSystemHelper.BytesPerSector; + information.TotalAllocationUnits = m_fileSystem.Size / ClusterSize; + information.AvailableAllocationUnits = m_fileSystem.FreeSpace / ClusterSize; + information.SectorsPerAllocationUnit = ClusterSize / BytesPerSector; + information.BytesPerSector = BytesPerSector; result = information; return NTStatus.STATUS_SUCCESS; } @@ -47,7 +47,7 @@ namespace SMBLibrary.Server FileFsAttributeInformation information = new FileFsAttributeInformation(); information.FileSystemAttributes = FileSystemAttributes.UnicodeOnDisk; information.MaximumComponentNameLength = 255; - information.FileSystemName = fileSystem.Name; + information.FileSystemName = m_fileSystem.Name; result = information; return NTStatus.STATUS_SUCCESS; } @@ -63,11 +63,11 @@ namespace SMBLibrary.Server case FileSystemInformationClass.FileFsFullSizeInformation: { FileFsFullSizeInformation information = new FileFsFullSizeInformation(); - information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize; - information.CallerAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; - information.ActualAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; - information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector; - information.BytesPerSector = NTFileSystemHelper.BytesPerSector; + information.TotalAllocationUnits = m_fileSystem.Size / ClusterSize; + information.CallerAvailableAllocationUnits = m_fileSystem.FreeSpace / ClusterSize; + information.ActualAvailableAllocationUnits = m_fileSystem.FreeSpace / ClusterSize; + information.SectorsPerAllocationUnit = ClusterSize / BytesPerSector; + information.BytesPerSector = BytesPerSector; result = information; return NTStatus.STATUS_SUCCESS; } @@ -81,10 +81,10 @@ namespace SMBLibrary.Server case FileSystemInformationClass.FileFsSectorSizeInformation: { FileFsSectorSizeInformation information = new FileFsSectorSizeInformation(); - information.LogicalBytesPerSector = NTFileSystemHelper.BytesPerSector; - information.PhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector; - information.PhysicalBytesPerSectorForPerformance = NTFileSystemHelper.BytesPerSector; - information.FileSystemEffectivePhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector; + information.LogicalBytesPerSector = BytesPerSector; + information.PhysicalBytesPerSectorForAtomicity = BytesPerSector; + information.PhysicalBytesPerSectorForPerformance = BytesPerSector; + information.FileSystemEffectivePhysicalBytesPerSectorForAtomicity = BytesPerSector; information.ByteOffsetForSectorAlignment = 0; information.ByteOffsetForPartitionAlignment = 0; result = information; diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Set.cs b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.Set.cs similarity index 63% rename from SMBLibrary/Server/Helpers/NTFileSystemHelper.Set.cs rename to SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.Set.cs index f549172..685e4f9 100644 --- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Set.cs +++ b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.Set.cs @@ -9,12 +9,13 @@ using System.Collections.Generic; using System.IO; using Utilities; -namespace SMBLibrary.Server +namespace SMBLibrary { - public partial class NTFileSystemHelper + public partial class NTFileSystemAdapter { - public static NTStatus SetFileInformation(IFileSystem fileSystem, OpenFileObject openFile, FileInformation information, ConnectionState state) + public NTStatus SetFileInformation(object handle, FileInformation information) { + FileHandle fileHandle = (FileHandle)handle; if (information is FileBasicInformation) { FileBasicInformation basicInformation = (FileBasicInformation)information; @@ -23,23 +24,23 @@ namespace SMBLibrary.Server bool isArchived = (basicInformation.FileAttributes & FileAttributes.Archive) > 0; try { - fileSystem.SetAttributes(openFile.Path, isHidden, isReadonly, isArchived); + m_fileSystem.SetAttributes(fileHandle.Path, isHidden, isReadonly, isArchived); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file attributes on '{0}'. {1}.", openFile.Path, status); + Log(Severity.Debug, "SetFileInformation: Failed to set file attributes on '{0}'. {1}.", fileHandle.Path, status); return status; } try { - fileSystem.SetDates(openFile.Path, basicInformation.CreationTime, basicInformation.LastWriteTime, basicInformation.LastAccessTime); + m_fileSystem.SetDates(fileHandle.Path, basicInformation.CreationTime, basicInformation.LastWriteTime, basicInformation.LastAccessTime); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. {1}.", openFile.Path, status); + Log(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. {1}.", fileHandle.Path, status); return status; } return NTStatus.STATUS_SUCCESS; @@ -52,28 +53,28 @@ namespace SMBLibrary.Server { destination = @"\" + destination; } - - if (openFile.Stream != null) + + if (fileHandle.Stream != null) { - openFile.Stream.Close(); + fileHandle.Stream.Close(); } // Note: it's possible that we just want to upcase / downcase a filename letter. try { - if (renameInformation.ReplaceIfExists && (fileSystem.GetEntry(destination) != null )) + if (renameInformation.ReplaceIfExists && (m_fileSystem.GetEntry(destination) != null)) { - fileSystem.Delete(destination); + m_fileSystem.Delete(destination); } - fileSystem.Move(openFile.Path, destination); + m_fileSystem.Move(fileHandle.Path, destination); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. {1}.", openFile.Path, status); + Log(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. {1}.", fileHandle.Path, status); return status; } - openFile.Path = destination; + fileHandle.Path = destination; return NTStatus.STATUS_SUCCESS; } else if (information is FileDispositionInformation) @@ -81,20 +82,20 @@ namespace SMBLibrary.Server if (((FileDispositionInformation)information).DeletePending) { // We're supposed to delete the file on close, but it's too late to report errors at this late stage - if (openFile.Stream != null) + if (fileHandle.Stream != null) { - openFile.Stream.Close(); + fileHandle.Stream.Close(); } try { - state.LogToServer(Severity.Information, "SetFileInformation: Deleting file '{0}'", openFile.Path); - fileSystem.Delete(openFile.Path); + Log(Severity.Information, "SetFileInformation: Deleting file '{0}'", fileHandle.Path); + m_fileSystem.Delete(fileHandle.Path); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "SetFileInformation: Error deleting '{0}'. {1}.", openFile.Path, status); + Log(Severity.Information, "SetFileInformation: Error deleting '{0}'. {1}.", status); return status; } } @@ -105,12 +106,12 @@ namespace SMBLibrary.Server long allocationSize = ((FileAllocationInformation)information).AllocationSize; try { - openFile.Stream.SetLength(allocationSize); + fileHandle.Stream.SetLength(allocationSize); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. {1}.", openFile.Path, status); + Log(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. {1}.", fileHandle.Path, status); return status; } return NTStatus.STATUS_SUCCESS; @@ -120,12 +121,12 @@ namespace SMBLibrary.Server long endOfFile = ((FileEndOfFileInformation)information).EndOfFile; try { - openFile.Stream.SetLength(endOfFile); + fileHandle.Stream.SetLength(endOfFile); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. {1}.", openFile.Path, status); + Log(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. {1}.", fileHandle.Path, status); return status; } return NTStatus.STATUS_SUCCESS; diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs similarity index 64% rename from SMBLibrary/Server/Helpers/NTFileSystemHelper.cs rename to SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs index 60b25d1..6de7bb3 100644 --- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs +++ b/SMBLibrary/NTFileStore/Adapter/NTFileSystemAdapter.cs @@ -7,23 +7,28 @@ using System; using System.Collections.Generic; using System.IO; -using SMBLibrary.Services; using Utilities; -namespace SMBLibrary.Server +namespace SMBLibrary { - /// - /// Helper class to access the FileSystemShare / NamedPipeShare in an NT-like manner dictated by the SMB protocol - /// - public partial class NTFileSystemHelper + public partial class NTFileSystemAdapter : INTFileStore { - public const int BytesPerSector = 512; - public const int ClusterSize = 4096; + private const int BytesPerSector = 512; + private const int ClusterSize = 4096; - public static NTStatus CreateFile(out FileSystemEntry entry, out Stream stream, out FileStatus fileStatus, IFileSystem fileSystem, string path, AccessMask desiredAccess, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, ConnectionState state) + private IFileSystem m_fileSystem; + + public event EventHandler OnLogEntry; + + public NTFileSystemAdapter(IFileSystem fileSystem) { + m_fileSystem = fileSystem; + } + + public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions) + { + handle = null; fileStatus = FileStatus.FILE_DOES_NOT_EXIST; - stream = null; FileAccess createAccess = NTFileStoreHelper.ToCreateFileAccess(desiredAccess, createDisposition); bool requestedWriteAccess = (createAccess & FileAccess.Write) > 0; @@ -35,7 +40,6 @@ namespace SMBLibrary.Server createDisposition != CreateDisposition.FILE_OPEN_IF && createDisposition != CreateDisposition.FILE_SUPERSEDE)) { - entry = null; return NTStatus.STATUS_INVALID_PARAMETER; } @@ -43,19 +47,18 @@ namespace SMBLibrary.Server if (path.Contains(":")) { // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND - entry = null; return NTStatus.STATUS_NO_SUCH_FILE; } + FileSystemEntry entry; try { - entry = fileSystem.GetEntry(path); + entry = m_fileSystem.GetEntry(path); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "CreateFile: Error retrieving '{0}'. {1}.", path, status); - entry = null; + Log(Severity.Debug, "CreateFile: Error retrieving '{0}'. {1}.", path, status); return status; } @@ -82,7 +85,7 @@ namespace SMBLibrary.Server if (entry != null) { // File already exists, fail the request - state.LogToServer(Severity.Debug, "CreateFile: File '{0}' already exist", path); + Log(Severity.Debug, "CreateFile: File '{0}' already exist", path); fileStatus = FileStatus.FILE_EXISTS; return NTStatus.STATUS_OBJECT_NAME_COLLISION; } @@ -96,19 +99,19 @@ namespace SMBLibrary.Server { if (forceDirectory) { - state.LogToServer(Severity.Information, "CreateFile: Creating directory '{0}'", path); - entry = fileSystem.CreateDirectory(path); + Log(Severity.Information, "CreateFile: Creating directory '{0}'", path); + entry = m_fileSystem.CreateDirectory(path); } else { - state.LogToServer(Severity.Information, "CreateFile: Creating file '{0}'", path); - entry = fileSystem.CreateFile(path); + Log(Severity.Information, "CreateFile: Creating file '{0}'", path); + entry = m_fileSystem.CreateFile(path); } } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status); + Log(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status); return status; } fileStatus = FileStatus.FILE_CREATED; @@ -134,19 +137,19 @@ namespace SMBLibrary.Server { if (forceDirectory) { - state.LogToServer(Severity.Information, "CreateFile: Creating directory '{0}'", path); - entry = fileSystem.CreateDirectory(path); + Log(Severity.Information, "CreateFile: Creating directory '{0}'", path); + entry = m_fileSystem.CreateDirectory(path); } else { - state.LogToServer(Severity.Information, "CreateFile: Creating file '{0}'", path); - entry = fileSystem.CreateFile(path); + Log(Severity.Information, "CreateFile: Creating file '{0}'", path); + entry = m_fileSystem.CreateFile(path); } } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status); + Log(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status); return status; } fileStatus = FileStatus.FILE_CREATED; @@ -165,13 +168,13 @@ namespace SMBLibrary.Server // Truncate the file try { - Stream temp = fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); + Stream temp = m_fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); temp.Close(); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "CreateFile: Error truncating '{0}'. {1}.", path, status); + Log(Severity.Debug, "CreateFile: Error truncating '{0}'. {1}.", path, status); return status; } fileStatus = FileStatus.FILE_OVERWRITTEN; @@ -181,12 +184,12 @@ namespace SMBLibrary.Server // Delete the old file try { - fileSystem.Delete(path); + m_fileSystem.Delete(path); } catch(Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "CreateFile: Error deleting '{0}'. {1}.", path, status); + Log(Severity.Debug, "CreateFile: Error deleting '{0}'. {1}.", path, status); return status; } @@ -194,19 +197,19 @@ namespace SMBLibrary.Server { if (forceDirectory) { - state.LogToServer(Severity.Information, "CreateFile: Creating directory '{0}'", path); - entry = fileSystem.CreateDirectory(path); + Log(Severity.Information, "CreateFile: Creating directory '{0}'", path); + entry = m_fileSystem.CreateDirectory(path); } else { - state.LogToServer(Severity.Information, "CreateFile: Creating file '{0}'", path); - entry = fileSystem.CreateFile(path); + Log(Severity.Information, "CreateFile: Creating file '{0}'", path); + entry = m_fileSystem.CreateFile(path); } } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status); + Log(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status); return status; } fileStatus = FileStatus.FILE_SUPERSEDED; @@ -219,6 +222,7 @@ namespace SMBLibrary.Server } FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(desiredAccess.File); + Stream stream; bool deleteOnClose = false; if (fileAccess == (FileAccess)0 || entry.IsDirectory) { @@ -227,13 +231,14 @@ namespace SMBLibrary.Server else { deleteOnClose = (createOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0; - NTStatus openStatus = OpenFileStream(out stream, fileSystem, path, fileAccess, shareAccess, createOptions, state); + NTStatus openStatus = OpenFileStream(out stream, path, fileAccess, shareAccess, createOptions); if (openStatus != NTStatus.STATUS_SUCCESS) { return openStatus; } } + handle = new FileHandle(path, entry.IsDirectory, stream, deleteOnClose); if (fileStatus != FileStatus.FILE_CREATED && fileStatus != FileStatus.FILE_OVERWRITTEN && fileStatus != FileStatus.FILE_SUPERSEDED) @@ -243,7 +248,7 @@ namespace SMBLibrary.Server return NTStatus.STATUS_SUCCESS; } - public static NTStatus OpenFileStream(out Stream stream, IFileSystem fileSystem, string path, FileAccess fileAccess, ShareAccess shareAccess, CreateOptions openOptions, ConnectionState state) + 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. @@ -253,15 +258,15 @@ namespace SMBLibrary.Server bool disableBuffering = (openOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0; bool buffered = (openOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint; FileShare fileShare = NTFileStoreHelper.ToFileShare(shareAccess); - state.LogToServer(Severity.Verbose, "OpenFile: Opening '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShare, buffered); + Log(Severity.Verbose, "OpenFileStream: Opening '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShare, buffered); try { - stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare); + stream = m_fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare); } catch (Exception ex) { NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "OpenFile: Cannot open '{0}'. {1}.", path, status); + Log(Severity.Debug, "OpenFile: Cannot open '{0}'. {1}.", path, status); return status; } @@ -273,86 +278,111 @@ namespace SMBLibrary.Server return NTStatus.STATUS_SUCCESS; } - public static NTStatus ReadFile(out byte[] data, OpenFileObject openFile, long offset, int maxCount, ConnectionState state) + public NTStatus CloseFile(object handle) { - data = null; - string openFilePath = openFile.Path; - Stream stream = openFile.Stream; - if (stream is RPCPipeStream) + FileHandle fileHandle = (FileHandle)handle; + if (fileHandle.Stream != null) { - data = new byte[maxCount]; - int bytesRead = stream.Read(data, 0, maxCount); - if (bytesRead < maxCount) - { - // EOF, we must trim the response data array - data = ByteReader.ReadBytes(data, 0, bytesRead); - } - return NTStatus.STATUS_SUCCESS; + fileHandle.Stream.Close(); } - else // File - { - if (stream == null || !stream.CanRead) - { - state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}', Invalid Operation.", openFilePath); - return NTStatus.STATUS_ACCESS_DENIED; - } - int bytesRead; + if (fileHandle.DeleteOnClose) + { try { - stream.Seek(offset, SeekOrigin.Begin); - data = new byte[maxCount]; - bytesRead = stream.Read(data, 0, maxCount); + m_fileSystem.Delete(fileHandle.Path); } - catch (Exception ex) + catch { - NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}'. {1}.", openFilePath, status); - return status; } + } + return NTStatus.STATUS_SUCCESS; + } - if (bytesRead < maxCount) - { - // EOF, we must trim the response data array - data = ByteReader.ReadBytes(data, 0, bytesRead); - } - return NTStatus.STATUS_SUCCESS; + public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount) + { + data = null; + FileHandle fileHandle = (FileHandle)handle; + string path = fileHandle.Path; + Stream stream = fileHandle.Stream; + if (stream == null || !stream.CanRead) + { + Log(Severity.Debug, "ReadFile: Cannot read '{0}', Invalid Operation.", path); + return NTStatus.STATUS_ACCESS_DENIED; + } + + int bytesRead; + try + { + stream.Seek(offset, SeekOrigin.Begin); + data = new byte[maxCount]; + bytesRead = stream.Read(data, 0, maxCount); + } + catch (Exception ex) + { + NTStatus status = ToNTStatus(ex); + Log(Severity.Debug, "ReadFile: Cannot read '{0}'. {1}.", path, status); + return status; + } + + if (bytesRead < maxCount) + { + // EOF, we must trim the response data array + data = ByteReader.ReadBytes(data, 0, bytesRead); + } + return NTStatus.STATUS_SUCCESS; + } + + public NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data) + { + numberOfBytesWritten = 0; + FileHandle fileHandle = (FileHandle)handle; + string path = fileHandle.Path; + Stream stream = fileHandle.Stream; + if (stream == null || !stream.CanWrite) + { + Log(Severity.Debug, "WriteFile: Cannot write '{0}'. Invalid Operation.", path); + return NTStatus.STATUS_ACCESS_DENIED; + } + + try + { + stream.Seek(offset, SeekOrigin.Begin); + stream.Write(data, 0, data.Length); + } + catch (Exception ex) + { + NTStatus status = ToNTStatus(ex); + Log(Severity.Debug, "WriteFile: Cannot write '{0}'. {1}.", path, status); + return status; + } + numberOfBytesWritten = data.Length; + return NTStatus.STATUS_SUCCESS; + } + + public NTStatus FlushFileBuffers(object handle) + { + FileHandle fileHandle = (FileHandle)handle; + if (fileHandle.Stream != null) + { + fileHandle.Stream.Flush(); + } + return NTStatus.STATUS_SUCCESS; + } + + public void Log(Severity severity, string message) + { + // To be thread-safe we must capture the delegate reference first + EventHandler handler = OnLogEntry; + if (handler != null) + { + handler(this, new LogEntry(DateTime.Now, severity, "NT FileSystem", message)); } } - public static NTStatus WriteFile(out int numberOfBytesWritten, OpenFileObject openFile, long offset, byte[] data, ConnectionState state) + public void Log(Severity severity, string message, params object[] args) { - numberOfBytesWritten = 0; - string openFilePath = openFile.Path; - Stream stream = openFile.Stream; - if (stream is RPCPipeStream) - { - stream.Write(data, 0, data.Length); - numberOfBytesWritten = data.Length; - return NTStatus.STATUS_SUCCESS; - } - else // File - { - if (stream == null || !stream.CanWrite) - { - state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Invalid Operation.", openFilePath); - return NTStatus.STATUS_ACCESS_DENIED; - } - - try - { - stream.Seek(offset, SeekOrigin.Begin); - stream.Write(data, 0, data.Length); - } - catch (Exception ex) - { - NTStatus status = ToNTStatus(ex); - state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. {1}.", openFilePath, status); - return status; - } - numberOfBytesWritten = data.Length; - return NTStatus.STATUS_SUCCESS; - } + Log(severity, String.Format(message, args)); } /// IFileSystem exception diff --git a/SMBLibrary/NTFileStore/INTFileStore.cs b/SMBLibrary/NTFileStore/INTFileStore.cs new file mode 100644 index 0000000..d47f584 --- /dev/null +++ b/SMBLibrary/NTFileStore/INTFileStore.cs @@ -0,0 +1,37 @@ +/* Copyright (C) 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 Utilities; + +namespace SMBLibrary +{ + /// + /// A file store (a.k.a. object store) interface to allow access to a file system or a named pipe in an NT-like manner dictated by the SMB protocol. + /// + public interface INTFileStore + { + NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions); + + NTStatus CloseFile(object handle); + + NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount); + + NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data); + + NTStatus FlushFileBuffers(object handle); + + NTStatus QueryDirectory(out List result, object handle, string fileName, FileInformationClass informationClass); + + NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass); + + NTStatus SetFileInformation(object handle, FileInformation information); + + NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass); + } +} diff --git a/SMBLibrary/NTFileStore/NTFileStoreHelper.cs b/SMBLibrary/NTFileStore/NTFileStoreHelper.cs index d9e67e8..9b6ddc3 100644 --- a/SMBLibrary/NTFileStore/NTFileStoreHelper.cs +++ b/SMBLibrary/NTFileStore/NTFileStoreHelper.cs @@ -98,5 +98,36 @@ namespace SMBLibrary return result; } + + public static FileNetworkOpenInformation GetNetworkOpenInformation(INTFileStore fileStore, string path) + { + object handle; + FileStatus fileStatus; + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, path, FileAccessMask.FILE_READ_ATTRIBUTES, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, 0); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + return null; + } + FileInformation fileInfo; + NTStatus queryStatus = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileNetworkOpenInformation); + fileStore.CloseFile(handle); + if (queryStatus != NTStatus.STATUS_SUCCESS) + { + return null; + } + return (FileNetworkOpenInformation)fileInfo; + } + + public static FileNetworkOpenInformation GetNetworkOpenInformation(INTFileStore fileStore, object handle) + { + FileInformation fileInfo; + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileNetworkOpenInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return null; + } + + return (FileNetworkOpenInformation)fileInfo; + } } } diff --git a/SMBLibrary/NTFileStore/NamedPipeStore.cs b/SMBLibrary/NTFileStore/NamedPipeStore.cs new file mode 100644 index 0000000..da9f7f2 --- /dev/null +++ b/SMBLibrary/NTFileStore/NamedPipeStore.cs @@ -0,0 +1,150 @@ +/* Copyright (C) 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 SMBLibrary.RPC; +using SMBLibrary.Services; +using Utilities; + +namespace SMBLibrary +{ + public class NamedPipeStore : INTFileStore + { + private List m_services; + + public NamedPipeStore(List services) + { + m_services = services; + } + + public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions) + { + fileStatus = FileStatus.FILE_DOES_NOT_EXIST; + // It is possible to have a named pipe that does not use RPC (e.g. MS-WSP), + // However this is not currently needed by our implementation. + RemoteService service = GetService(path); + if (service != null) + { + // All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles, + // and provides a separate conduit for client/server communication. + RPCPipeStream stream = new RPCPipeStream(service); + handle = new FileHandle(path, false, stream, false); + fileStatus = FileStatus.FILE_OPENED; + return NTStatus.STATUS_SUCCESS; + } + handle = null; + return NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; + } + + public NTStatus CloseFile(object handle) + { + FileHandle fileHandle = (FileHandle)handle; + if (fileHandle.Stream != null) + { + fileHandle.Stream.Close(); + } + return NTStatus.STATUS_SUCCESS; + } + + private RemoteService GetService(string path) + { + if (path.StartsWith(@"\")) + { + path = path.Substring(1); + } + + foreach (RemoteService service in m_services) + { + if (String.Equals(path, service.PipeName, StringComparison.InvariantCultureIgnoreCase)) + { + return service; + } + } + return null; + } + + public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount) + { + Stream stream = ((FileHandle)handle).Stream; + data = new byte[maxCount]; + int bytesRead = stream.Read(data, 0, maxCount); + if (bytesRead < maxCount) + { + // EOF, we must trim the response data array + data = ByteReader.ReadBytes(data, 0, bytesRead); + } + return NTStatus.STATUS_SUCCESS; + } + + public NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data) + { + Stream stream = ((FileHandle)handle).Stream; + stream.Write(data, 0, data.Length); + numberOfBytesWritten = data.Length; + return NTStatus.STATUS_SUCCESS; + } + + public NTStatus FlushFileBuffers(object handle) + { + FileHandle fileHandle = (FileHandle)handle; + if (fileHandle.Stream != null) + { + fileHandle.Stream.Flush(); + } + return NTStatus.STATUS_SUCCESS; + } + + public NTStatus QueryDirectory(out List result, object directoryHandle, string fileName, FileInformationClass informationClass) + { + result = null; + return NTStatus.STATUS_NOT_SUPPORTED; + } + + public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass) + { + switch (informationClass) + { + case FileInformationClass.FileBasicInformation: + { + FileBasicInformation information = new FileBasicInformation(); + information.FileAttributes = FileAttributes.Temporary; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case FileInformationClass.FileStandardInformation: + { + FileStandardInformation information = new FileStandardInformation(); + information.DeletePending = false; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case FileInformationClass.FileNetworkOpenInformation: + { + FileNetworkOpenInformation information = new FileNetworkOpenInformation(); + information.FileAttributes = FileAttributes.Temporary; + result = information; + return NTStatus.STATUS_SUCCESS; + } + default: + result = null; + return NTStatus.STATUS_INVALID_INFO_CLASS; + } + } + + public NTStatus SetFileInformation(object handle, FileInformation information) + { + return NTStatus.STATUS_NOT_SUPPORTED; + } + + public NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass) + { + result = null; + return NTStatus.STATUS_NOT_SUPPORTED; + } + } +} diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj index b080c73..be6b2cb 100644 --- a/SMBLibrary/SMBLibrary.csproj +++ b/SMBLibrary/SMBLibrary.csproj @@ -77,7 +77,13 @@ + + + + + + @@ -91,6 +97,8 @@ + + @@ -174,11 +182,6 @@ - - - - - @@ -195,10 +198,11 @@ - - - - + + + + + diff --git a/SMBLibrary/Server/ConnectionState/OpenFileObject.cs b/SMBLibrary/Server/ConnectionState/OpenFileObject.cs index a43bea6..c5d3aea 100644 --- a/SMBLibrary/Server/ConnectionState/OpenFileObject.cs +++ b/SMBLibrary/Server/ConnectionState/OpenFileObject.cs @@ -14,14 +14,12 @@ namespace SMBLibrary.Server public class OpenFileObject { public string Path; - public Stream Stream; - public bool DeleteOnClose; + public object Handle; - public OpenFileObject(string path, Stream stream, bool deleteOnClose) + public OpenFileObject(string path, object handle) { Path = path; - Stream = stream; - DeleteOnClose = deleteOnClose; + Handle = handle; } } } diff --git a/SMBLibrary/Server/ConnectionState/OpenSearch.cs b/SMBLibrary/Server/ConnectionState/OpenSearch.cs index f74de3e..6722343 100644 --- a/SMBLibrary/Server/ConnectionState/OpenSearch.cs +++ b/SMBLibrary/Server/ConnectionState/OpenSearch.cs @@ -12,10 +12,10 @@ namespace SMBLibrary.Server { public class OpenSearch { - public List Entries; + public List Entries; public int EnumerationLocation; - public OpenSearch(List entries, int enumerationLocation) + public OpenSearch(List entries, int enumerationLocation) { Entries = entries; EnumerationLocation = enumerationLocation; diff --git a/SMBLibrary/Server/ConnectionState/SMB1Session.cs b/SMBLibrary/Server/ConnectionState/SMB1Session.cs index efd8514..67df3ed 100644 --- a/SMBLibrary/Server/ConnectionState/SMB1Session.cs +++ b/SMBLibrary/Server/ConnectionState/SMB1Session.cs @@ -70,17 +70,12 @@ namespace SMBLibrary.Server return AddOpenFile(relativePath, null); } - public ushort? AddOpenFile(string relativePath, Stream stream) - { - return AddOpenFile(relativePath, stream, false); - } - - public ushort? AddOpenFile(string relativePath, Stream stream, bool deleteOnClose) + public ushort? AddOpenFile(string relativePath, object handle) { ushort? fileID = m_connection.AllocateFileID(); if (fileID.HasValue) { - m_openFiles.Add(fileID.Value, new OpenFileObject(relativePath, stream, deleteOnClose)); + m_openFiles.Add(fileID.Value, new OpenFileObject(relativePath, handle)); } return fileID; } @@ -94,11 +89,6 @@ namespace SMBLibrary.Server public void RemoveOpenFile(ushort fileID) { - Stream stream = m_openFiles[fileID].Stream; - if (stream != null) - { - stream.Close(); - } m_openFiles.Remove(fileID); } @@ -120,7 +110,7 @@ namespace SMBLibrary.Server return null; } - public ushort? AddOpenSearch(List entries, int enumerationLocation) + public ushort? AddOpenSearch(List entries, int enumerationLocation) { ushort? searchHandle = AllocateSearchHandle(); if (searchHandle.HasValue) diff --git a/SMBLibrary/Server/ConnectionState/SMB2Session.cs b/SMBLibrary/Server/ConnectionState/SMB2Session.cs index 7a6d1f8..a97abb5 100644 --- a/SMBLibrary/Server/ConnectionState/SMB2Session.cs +++ b/SMBLibrary/Server/ConnectionState/SMB2Session.cs @@ -90,24 +90,13 @@ namespace SMBLibrary.Server return m_connectedTrees.ContainsKey(treeID); } - /// Should include the path relative to the share /// The persistent portion of the FileID - public ulong? AddOpenFile(string relativePath) - { - return AddOpenFile(relativePath, null); - } - - public ulong? AddOpenFile(string relativePath, Stream stream) - { - return AddOpenFile(relativePath, stream, false); - } - - public ulong? AddOpenFile(string relativePath, Stream stream, bool deleteOnClose) + public ulong? AddOpenFile(string relativePath, object handle) { ulong? persistentID = m_connection.AllocatePersistentFileID(); if (persistentID.HasValue) { - m_openFiles.Add(persistentID.Value, new OpenFileObject(relativePath, stream, deleteOnClose)); + m_openFiles.Add(persistentID.Value, new OpenFileObject(relativePath, handle)); } return persistentID; } @@ -126,16 +115,11 @@ namespace SMBLibrary.Server public void RemoveOpenFile(ulong fileID) { - Stream stream = m_openFiles[fileID].Stream; - if (stream != null) - { - stream.Close(); - } m_openFiles.Remove(fileID); m_openSearches.Remove(fileID); } - public OpenSearch AddOpenSearch(ulong fileID, List entries, int enumerationLocation) + public OpenSearch AddOpenSearch(ulong fileID, List entries, int enumerationLocation) { OpenSearch openSearch = new OpenSearch(entries, enumerationLocation); m_openSearches.Add(fileID, openSearch); diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs b/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs deleted file mode 100644 index 432308e..0000000 --- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright (C) 2014-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 Utilities; - -namespace SMBLibrary.Server -{ - public partial class NTFileSystemHelper - { - /// Expression as described in [MS-FSA] 2.1.4.4 - public static NTStatus FindEntries(out List entries, IFileSystem fileSystem, string path, string fileName) - { - entries = null; - FileSystemEntry entry = fileSystem.GetEntry(path); - if (entry == null) - { - return NTStatus.STATUS_NO_SUCH_FILE; - } - - if (!entry.IsDirectory) - { - return NTStatus.STATUS_INVALID_PARAMETER; - } - - if (fileName == String.Empty) - { - return NTStatus.STATUS_INVALID_PARAMETER; - } - - bool findExactName = !ContainsWildcardCharacters(fileName); - - if (!findExactName) - { - try - { - entries = fileSystem.ListEntriesInDirectory(path); - } - catch (Exception ex) - { - NTStatus status = ToNTStatus(ex); - return status; ; - } - - entries = GetFiltered(entries, fileName); - - // Windows will return "." and ".." when enumerating directory files. - // The SMB1 / SMB2 specifications mandate that when zero entries are found, the server SHOULD / MUST return STATUS_NO_SUCH_FILE. - // For this reason, we MUST include the current directory and/or parent directory when enumerating a directory - // in order to diffrentiate between a directory that does not exist and a directory with no entries. - FileSystemEntry currentDirectory = fileSystem.GetEntry(path); - currentDirectory.Name = "."; - FileSystemEntry parentDirectory = fileSystem.GetEntry(FileSystem.GetParentDirectory(path)); - parentDirectory.Name = ".."; - entries.Insert(0, parentDirectory); - entries.Insert(0, currentDirectory); - } - else - { - path = FileSystem.GetDirectoryPath(path); - entry = fileSystem.GetEntry(path + fileName); - if (entry == null) - { - return NTStatus.STATUS_NO_SUCH_FILE; - } - entries = new List(); - entries.Add(entry); - } - return NTStatus.STATUS_SUCCESS; - } - - /// Expression as described in [MS-FSA] 2.1.4.4 - private static List GetFiltered(List entries, string expression) - { - if (expression == "*") - { - return entries; - } - - List result = new List(); - foreach (FileSystemEntry entry in entries) - { - if (IsFileNameInExpression(entry.Name, expression)) - { - result.Add(entry); - } - } - return result; - } - - private static bool ContainsWildcardCharacters(string expression) - { - return (expression.Contains("?") || expression.Contains("*") || expression.Contains("\"") || expression.Contains(">") || expression.Contains("<")); - } - - // [MS-FSA] 2.1.4.4 - // The FileName is string compared with Expression using the following wildcard rules: - // * (asterisk) Matches zero or more characters. - // ? (question mark) Matches a single character. - // DOS_DOT (" quotation mark) Matches either a period or zero characters beyond the name string. - // DOS_QM (> greater than) Matches any single character or, upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs. - // DOS_STAR (< less than) Matches zero or more characters until encountering and matching the final . in the name. - private static bool IsFileNameInExpression(string fileName, string expression) - { - if (expression == "*") - { - return true; - } - else if (expression.EndsWith("*")) // expression.Length > 1 - { - string desiredFileNameStart = expression.Substring(0, expression.Length - 1); - bool findExactNameWithoutExtension = false; - if (desiredFileNameStart.EndsWith("\"")) - { - findExactNameWithoutExtension = true; - desiredFileNameStart = desiredFileNameStart.Substring(0, desiredFileNameStart.Length - 1); - } - - if (!findExactNameWithoutExtension) - { - if (fileName.StartsWith(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - else - { - if (fileName.StartsWith(desiredFileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) || - fileName.Equals(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - } - else if (expression.StartsWith("<")) - { - string desiredFileNameEnd = expression.Substring(1); - if (fileName.EndsWith(desiredFileNameEnd, StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - else if (String.Equals(fileName, expression, StringComparison.CurrentCultureIgnoreCase)) - { - return true; - } - return false; - } - } -} diff --git a/SMBLibrary/Server/SMB1/FileSystemResponseHelper.cs b/SMBLibrary/Server/SMB1/FileSystemResponseHelper.cs index a86777b..590b50f 100644 --- a/SMBLibrary/Server/SMB1/FileSystemResponseHelper.cs +++ b/SMBLibrary/Server/SMB1/FileSystemResponseHelper.cs @@ -23,22 +23,10 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } - IFileSystem fileSystem = share.FileSystem; - try + header.Status = SMB1FileStoreHelper.CreateDirectory(share.FileStore, request.DirectoryName); + if (header.Status != NTStatus.STATUS_SUCCESS) { - fileSystem.CreateDirectory(request.DirectoryName); - } - catch (IOException) - { - state.LogToServer(Severity.Debug, "CreateDirectory: Cannot create '{0}'", request.DirectoryName); - header.Status = NTStatus.STATUS_OBJECT_NAME_INVALID; - return new ErrorResponse(request.CommandName); - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "CreateDirectory: Cannot create '{0}', Access Denied", request.DirectoryName); - header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } @@ -53,51 +41,13 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(request.DirectoryName); - if (entry == null) + header.Status = SMB1FileStoreHelper.DeleteDirectory(share.FileStore, request.DirectoryName); + if (header.Status != NTStatus.STATUS_SUCCESS) { - header.Status = NTStatus.STATUS_NO_SUCH_FILE; return new ErrorResponse(request.CommandName); } - - if (!entry.IsDirectory) - { - header.Status = NTStatus.STATUS_OBJECT_PATH_INVALID; - return new ErrorResponse(request.CommandName); - } - - try - { - fileSystem.Delete(request.DirectoryName); - return new DeleteDirectoryResponse(); - } - catch (IOException) - { - state.LogToServer(Severity.Debug, "DeleteDirectory: Cannot delete '{0}'", request.DirectoryName); - header.Status = NTStatus.STATUS_CANNOT_DELETE; - return new ErrorResponse(request.CommandName); - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "DeleteDirectory: Cannot delete '{0}', Access Denied", request.DirectoryName); - header.Status = NTStatus.STATUS_ACCESS_DENIED; - return new ErrorResponse(request.CommandName); - } - } - - internal static SMB1Command GetCheckDirectoryResponse(SMB1Header header, CheckDirectoryRequest request, FileSystemShare share) - { - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(request.DirectoryName); - if (entry == null || !entry.IsDirectory) - { - header.Status = NTStatus.STATUS_NO_SUCH_FILE; - return new ErrorResponse(request.CommandName); - } - - return new CheckDirectoryResponse(); + return new DeleteDirectoryResponse(); } internal static SMB1Command GetDeleteResponse(SMB1Header header, DeleteRequest request, FileSystemShare share, SMB1ConnectionState state) @@ -108,39 +58,14 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(request.FileName); - if (entry == null) + // [MS-CIFS] This command cannot delete directories or volumes. + header.Status = SMB1FileStoreHelper.DeleteFile(share.FileStore, request.FileName); + if (header.Status != NTStatus.STATUS_SUCCESS) { - header.Status = NTStatus.STATUS_NO_SUCH_FILE; - return new ErrorResponse(request.CommandName); - } - - if (!entry.IsDirectory && (request.SearchAttributes & SMBFileAttributes.Directory) > 0 - || entry.IsDirectory && (request.SearchAttributes & SMBFileAttributes.Directory) == 0) - { - header.Status = NTStatus.STATUS_OBJECT_PATH_INVALID; - return new ErrorResponse(request.CommandName); - } - - try - { - fileSystem.Delete(request.FileName); - return new DeleteResponse(); - } - catch (IOException) - { - state.LogToServer(Severity.Debug, "Delete: Cannot delete '{0}'", request.FileName); - header.Status = NTStatus.STATUS_CANNOT_DELETE; - return new ErrorResponse(request.CommandName); - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "DeleteDirectory: Cannot delete '{0}', Access Denied", request.FileName); - header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } + return new DeleteResponse(); } internal static SMB1Command GetRenameResponse(SMB1Header header, RenameRequest request, FileSystemShare share, SMB1ConnectionState state) @@ -156,59 +81,53 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry sourceEntry = fileSystem.GetEntry(request.OldFileName); - if (sourceEntry == null) + header.Status = SMB1FileStoreHelper.Rename(share.FileStore, request.OldFileName, request.NewFileName, request.SearchAttributes); + if (header.Status != NTStatus.STATUS_SUCCESS) { - header.Status = NTStatus.STATUS_NO_SUCH_FILE; return new ErrorResponse(request.CommandName); } + return new RenameResponse(); + } - // The file must not already exist unless we just want to upcase / downcase a filename letter - FileSystemEntry destinationEntry = fileSystem.GetEntry(request.NewFileName); - if (destinationEntry != null && - !String.Equals(request.OldFileName, request.NewFileName, StringComparison.InvariantCultureIgnoreCase)) + internal static SMB1Command GetCheckDirectoryResponse(SMB1Header header, CheckDirectoryRequest request, FileSystemShare share, SMB1ConnectionState state) + { + SMB1Session session = state.GetSession(header.UID); + if (!share.HasReadAccess(session.UserName, request.DirectoryName, state.ClientEndPoint)) { - // The new file already exists. - header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION; - return new ErrorResponse(request.CommandName); - } - - try - { - fileSystem.Move(request.OldFileName, request.NewFileName); - return new RenameResponse(); - } - catch (IOException) - { - state.LogToServer(Severity.Debug, "Rename: Sharing violation renaming '{0}'", request.OldFileName); - header.Status = NTStatus.STATUS_SHARING_VIOLATION; - return new ErrorResponse(request.CommandName); - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "Rename: Cannot rename '{0}', Access Denied", request.OldFileName); header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } + + header.Status = SMB1FileStoreHelper.CheckDirectory(share.FileStore, request.DirectoryName); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(request.CommandName); + } + + return new CheckDirectoryResponse(); } - internal static SMB1Command GetQueryInformationResponse(SMB1Header header, QueryInformationRequest request, FileSystemShare share) + internal static SMB1Command GetQueryInformationResponse(SMB1Header header, QueryInformationRequest request, FileSystemShare share, SMB1ConnectionState state) { - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(request.FileName); - if (entry == null) + SMB1Session session = state.GetSession(header.UID); + if (!share.HasReadAccess(session.UserName, request.FileName, state.ClientEndPoint)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(request.CommandName); + } + + FileNetworkOpenInformation fileInfo; + header.Status = SMB1FileStoreHelper.QueryInformation(out fileInfo, share.FileStore, request.FileName); + if (header.Status != NTStatus.STATUS_SUCCESS) { - header.Status = NTStatus.STATUS_OBJECT_PATH_INVALID; return new ErrorResponse(request.CommandName); } QueryInformationResponse response = new QueryInformationResponse(); - response.FileAttributes = SMB1FileSystemHelper.GetFileAttributes(entry); - response.LastWriteTime = entry.LastWriteTime; - response.FileSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); - + response.FileAttributes = SMB1FileStoreHelper.GetFileAttributes(fileInfo.FileAttributes); + response.LastWriteTime = fileInfo.LastWriteTime; + response.FileSize = (uint)Math.Min(UInt32.MaxValue, fileInfo.EndOfFile); return response; } @@ -220,37 +139,13 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(request.FileName); - if (entry == null) + header.Status = SMB1FileStoreHelper.SetInformation(share.FileStore, request.FileName, request.FileAttributes, request.LastWriteTime); + if (header.Status != NTStatus.STATUS_SUCCESS) { - header.Status = NTStatus.STATUS_NO_SUCH_FILE; return new ErrorResponse(request.CommandName); } - bool? isHidden = null; - bool? isReadOnly = null; - bool? isArchived = null; - if ((request.FileAttributes & SMBFileAttributes.Hidden) > 0) - { - isHidden = true; - } - if ((request.FileAttributes & SMBFileAttributes.ReadOnly) > 0) - { - isReadOnly = true; - } - if ((request.FileAttributes & SMBFileAttributes.Archive) > 0) - { - isArchived = true; - } - fileSystem.SetAttributes(request.FileName, isHidden, isReadOnly, isArchived); - - if (request.LastWriteTime.HasValue) - { - fileSystem.SetDates(request.FileName, null, request.LastWriteTime, null); - } - return new SetInformationResponse(); } @@ -269,9 +164,13 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return new ErrorResponse(request.CommandName); } - IFileSystem fileSystem = share.FileSystem; - fileSystem.SetDates(openFile.Path, request.CreationDateTime, request.LastWriteDateTime, request.LastAccessDateTime); + header.Status = SMB1FileStoreHelper.SetInformation2(share.FileStore, openFile.Handle, request.CreationDateTime, request.LastAccessDateTime, request.LastWriteDateTime); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(request.CommandName); + } + return new SetInformation2Response(); } } diff --git a/SMBLibrary/Server/SMB1/NTCreateHelper.cs b/SMBLibrary/Server/SMB1/NTCreateHelper.cs index bfeb61f..336ade0 100644 --- a/SMBLibrary/Server/SMB1/NTCreateHelper.cs +++ b/SMBLibrary/Server/SMB1/NTCreateHelper.cs @@ -31,60 +31,40 @@ namespace SMBLibrary.Server.SMB1 } } + object handle; + FileStatus fileStatus; + NTStatus createStatus = share.FileStore.CreateFile(out handle, out fileStatus, path, request.DesiredAccess, request.ShareAccess, request.CreateDisposition, request.CreateOptions); + if (createStatus != NTStatus.STATUS_SUCCESS) + { + header.Status = createStatus; + return new ErrorResponse(request.CommandName); + } + + ushort? fileID = session.AddOpenFile(path, handle); + if (!fileID.HasValue) + { + share.FileStore.CloseFile(handle); + header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES; + return new ErrorResponse(request.CommandName); + } + if (share is NamedPipeShare) { - Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path); - if (pipeStream != null) + if (isExtended) { - ushort? fileID = session.AddOpenFile(path, pipeStream); - if (!fileID.HasValue) - { - header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES; - return new ErrorResponse(request.CommandName); - } - if (isExtended) - { - return CreateResponseExtendedForNamedPipe(fileID.Value, FileStatus.FILE_OPENED); - } - else - { - return CreateResponseForNamedPipe(fileID.Value, FileStatus.FILE_OPENED); - } + return CreateResponseExtendedForNamedPipe(fileID.Value, FileStatus.FILE_OPENED); + } + else + { + return CreateResponseForNamedPipe(fileID.Value, FileStatus.FILE_OPENED); } - - header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; - return new ErrorResponse(request.CommandName); } else // FileSystemShare { - FileSystemShare fileSystemShare = (FileSystemShare)share; - - FileSystemEntry entry; - Stream stream; - FileStatus fileStatus; - NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, out stream, out fileStatus, fileSystemShare.FileSystem, path, request.DesiredAccess, request.ShareAccess, request.CreateDisposition, request.CreateOptions, state); - if (createStatus != NTStatus.STATUS_SUCCESS) - { - header.Status = createStatus; - return new ErrorResponse(request.CommandName); - } - - FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(request.DesiredAccess); - - bool deleteOnClose = (stream != null) && ((request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0); - ushort? fileID = session.AddOpenFile(path, stream, deleteOnClose); - if (!fileID.HasValue) - { - if (stream != null) - { - stream.Close(); - } - header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES; - return new ErrorResponse(request.CommandName); - } + FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle); if (isExtended) { - NTCreateAndXResponseExtended response = CreateResponseExtendedFromFileSystemEntry(entry, fileID.Value, fileStatus); + NTCreateAndXResponseExtended response = CreateResponseExtendedFromFileInformation(fileInfo, fileID.Value, fileStatus); if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0) { response.OpLockLevel = OpLockLevel.BatchOpLockGranted; @@ -93,7 +73,7 @@ namespace SMBLibrary.Server.SMB1 } else { - NTCreateAndXResponse response = CreateResponseFromFileSystemEntry(entry, fileID.Value, fileStatus); + NTCreateAndXResponse response = CreateResponseFromFileInformation(fileInfo, fileID.Value, fileStatus); if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0) { response.OpLockLevel = OpLockLevel.BatchOpLockGranted; @@ -140,52 +120,38 @@ namespace SMBLibrary.Server.SMB1 return response; } - private static NTCreateAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID, FileStatus fileStatus) + private static NTCreateAndXResponse CreateResponseFromFileInformation(FileNetworkOpenInformation fileInfo, ushort fileID, FileStatus fileStatus) { NTCreateAndXResponse response = new NTCreateAndXResponse(); - if (entry.IsDirectory) - { - response.ExtFileAttributes = ExtendedFileAttributes.Directory; - response.Directory = true; - } - else - { - response.ExtFileAttributes = ExtendedFileAttributes.Normal; - } response.FID = fileID; response.CreateDisposition = ToCreateDisposition(fileStatus); - response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - response.EndOfFile = (long)entry.Size; - response.CreateTime = entry.CreationTime; - response.LastAccessTime = entry.LastAccessTime; - response.LastWriteTime = entry.LastWriteTime; - response.LastChangeTime = entry.LastWriteTime; + response.CreateTime = fileInfo.CreationTime; + response.LastAccessTime = fileInfo.LastAccessTime; + response.LastWriteTime = fileInfo.LastWriteTime; + response.LastChangeTime = fileInfo.LastWriteTime; + response.AllocationSize = fileInfo.AllocationSize; + response.EndOfFile = fileInfo.EndOfFile; + response.ExtFileAttributes = (ExtendedFileAttributes)fileInfo.FileAttributes; response.ResourceType = ResourceType.FileTypeDisk; + response.Directory = fileInfo.IsDirectory; return response; } - private static NTCreateAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID, FileStatus fileStatus) + private static NTCreateAndXResponseExtended CreateResponseExtendedFromFileInformation(FileNetworkOpenInformation fileInfo, ushort fileID, FileStatus fileStatus) { NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended(); - if (entry.IsDirectory) - { - response.ExtFileAttributes = ExtendedFileAttributes.Directory; - response.Directory = true; - } - else - { - response.ExtFileAttributes = ExtendedFileAttributes.Normal; - } response.FID = fileID; response.CreateDisposition = ToCreateDisposition(fileStatus); - response.CreateTime = entry.CreationTime; - response.LastAccessTime = entry.LastAccessTime; - response.LastWriteTime = entry.LastWriteTime; - response.LastChangeTime = entry.LastWriteTime; - response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - response.EndOfFile = (long)entry.Size; + response.CreateTime = fileInfo.CreationTime; + response.LastAccessTime = fileInfo.LastAccessTime; + response.LastWriteTime = fileInfo.LastWriteTime; + response.LastChangeTime = fileInfo.LastWriteTime; + response.ExtFileAttributes = (ExtendedFileAttributes)fileInfo.FileAttributes; + response.AllocationSize = fileInfo.AllocationSize; + response.EndOfFile = fileInfo.EndOfFile; response.ResourceType = ResourceType.FileTypeDisk; response.FileStatusFlags = FileStatusFlags.NO_EAS | FileStatusFlags.NO_SUBSTREAMS | FileStatusFlags.NO_REPARSETAG; + response.Directory = fileInfo.IsDirectory; response.MaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA | FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | FileAccessMask.FILE_EXECUTE | diff --git a/SMBLibrary/Server/SMB1/OpenAndXHelper.cs b/SMBLibrary/Server/SMB1/OpenAndXHelper.cs index f3eee27..104bbce 100644 --- a/SMBLibrary/Server/SMB1/OpenAndXHelper.cs +++ b/SMBLibrary/Server/SMB1/OpenAndXHelper.cs @@ -48,38 +48,18 @@ namespace SMBLibrary.Server.SMB1 } } - FileSystemEntry entry = null; - Stream stream = null; + object handle; FileStatus fileStatus; - if (share is NamedPipeShare) + header.Status = share.FileStore.CreateFile(out handle, out fileStatus, path, desiredAccess, shareAccess, createDisposition, createOptions); + if (header.Status != NTStatus.STATUS_SUCCESS) { - Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path); - if (pipeStream == null) - { - header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; - return new ErrorResponse(request.CommandName); - } - fileStatus = FileStatus.FILE_OPENED; - } - else // FileSystemShare - { - FileSystemShare fileSystemShare = (FileSystemShare)share; - IFileSystem fileSystem = fileSystemShare.FileSystem; - - header.Status = NTFileSystemHelper.CreateFile(out entry, out stream, out fileStatus, fileSystem, path, desiredAccess, shareAccess, createDisposition, createOptions, state); - if (header.Status != NTStatus.STATUS_SUCCESS) - { - return new ErrorResponse(request.CommandName); - } + return new ErrorResponse(request.CommandName); } - ushort? fileID = session.AddOpenFile(path, stream); + ushort? fileID = session.AddOpenFile(path, handle); if (!fileID.HasValue) { - if (stream != null) - { - stream.Close(); - } + share.FileStore.CloseFile(handle); header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES; return new ErrorResponse(request.CommandName); } @@ -98,13 +78,14 @@ namespace SMBLibrary.Server.SMB1 } else // FileSystemShare { + FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle); if (isExtended) { - return CreateResponseExtendedFromFileSystemEntry(entry, fileID.Value, openResult); + return CreateResponseExtendedFromFileInfo(fileInfo, fileID.Value, openResult); } else { - return CreateResponseFromFileSystemEntry(entry, fileID.Value, openResult); + return CreateResponseFromFileInfo(fileInfo, fileID.Value, openResult); } } } @@ -288,40 +269,26 @@ namespace SMBLibrary.Server.SMB1 return response; } - private static OpenAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID, OpenResult openResult) + private static OpenAndXResponse CreateResponseFromFileInfo(FileNetworkOpenInformation fileInfo, ushort fileID, OpenResult openResult) { OpenAndXResponse response = new OpenAndXResponse(); response.FID = fileID; - if (entry.IsDirectory) - { - response.FileAttrs = SMBFileAttributes.Directory; - } - else - { - response.FileAttrs = SMBFileAttributes.Normal; - } - response.LastWriteTime = entry.LastWriteTime; - response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); + response.FileAttrs = SMB1FileStoreHelper.GetFileAttributes(fileInfo.FileAttributes); + response.LastWriteTime = fileInfo.LastWriteTime; + response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, fileInfo.EndOfFile); response.AccessRights = AccessRights.SMB_DA_ACCESS_READ; response.ResourceType = ResourceType.FileTypeDisk; response.OpenResults.OpenResult = openResult; return response; } - private static OpenAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID, OpenResult openResult) + private static OpenAndXResponseExtended CreateResponseExtendedFromFileInfo(FileNetworkOpenInformation fileInfo, ushort fileID, OpenResult openResult) { OpenAndXResponseExtended response = new OpenAndXResponseExtended(); response.FID = fileID; - if (entry.IsDirectory) - { - response.FileAttrs = SMBFileAttributes.Directory; - } - else - { - response.FileAttrs = SMBFileAttributes.Normal; - } - response.LastWriteTime = entry.LastWriteTime; - response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); + response.FileAttrs = SMB1FileStoreHelper.GetFileAttributes(fileInfo.FileAttributes); + response.LastWriteTime = fileInfo.LastWriteTime; + response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, fileInfo.EndOfFile); response.AccessRights = AccessRights.SMB_DA_ACCESS_READ; response.ResourceType = ResourceType.FileTypeDisk; response.OpenResults.OpenResult = openResult; diff --git a/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs b/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs index 79a29cb..cd8a6fc 100644 --- a/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs +++ b/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs @@ -37,7 +37,7 @@ namespace SMBLibrary.Server.SMB1 } byte[] data; - header.Status = NTFileSystemHelper.ReadFile(out data, openFile, request.ReadOffsetInBytes, request.CountOfBytesToRead, state); + header.Status = share.FileStore.ReadFile(out data, openFile.Handle, request.ReadOffsetInBytes, request.CountOfBytesToRead); if (header.Status != NTStatus.STATUS_SUCCESS) { return new ErrorResponse(request.CommandName); @@ -74,7 +74,7 @@ namespace SMBLibrary.Server.SMB1 maxCount = request.MaxCountLarge; } byte[] data; - header.Status = NTFileSystemHelper.ReadFile(out data, openFile, (long)request.Offset, (int)maxCount, state); + header.Status = share.FileStore.ReadFile(out data, openFile.Handle, (long)request.Offset, (int)maxCount); if (header.Status != NTStatus.STATUS_SUCCESS) { return new ErrorResponse(request.CommandName); @@ -108,9 +108,9 @@ namespace SMBLibrary.Server.SMB1 return new ErrorResponse(request.CommandName); } } - + int numberOfBytesWritten; - header.Status = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, request.WriteOffsetInBytes, request.Data, state); + header.Status = share.FileStore.WriteFile(out numberOfBytesWritten, openFile.Handle, request.WriteOffsetInBytes, request.Data); if (header.Status != NTStatus.STATUS_SUCCESS) { return new ErrorResponse(request.CommandName); @@ -129,7 +129,7 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_INVALID_HANDLE; return new ErrorResponse(request.CommandName); } - + if (share is FileSystemShare) { if (!((FileSystemShare)share).HasWriteAccess(session.UserName, openFile.Path, state.ClientEndPoint)) @@ -140,7 +140,7 @@ namespace SMBLibrary.Server.SMB1 } int numberOfBytesWritten; - header.Status = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, (long)request.Offset, request.Data, state); + header.Status = share.FileStore.WriteFile(out numberOfBytesWritten, openFile.Handle, (long)request.Offset, request.Data); if (header.Status != NTStatus.STATUS_SUCCESS) { return new ErrorResponse(request.CommandName); diff --git a/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Query.cs b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Query.cs new file mode 100644 index 0000000..a961a91 --- /dev/null +++ b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Query.cs @@ -0,0 +1,200 @@ +/* Copyright (C) 2014-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.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server.SMB1 +{ + public partial class SMB1FileStoreHelper + { + public static NTStatus GetFileInformation(out QueryInformation result, INTFileStore fileStore, string path, QueryInformationLevel informationLevel) + { + object handle; + FileStatus fileStatus; + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, path, FileAccessMask.FILE_READ_ATTRIBUTES, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, 0); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + result = null; + return openStatus; + } + NTStatus returnStatus = GetFileInformation(out result, fileStore, handle, informationLevel); + fileStore.CloseFile(handle); + return returnStatus; + } + + public static NTStatus GetFileInformation(out QueryInformation result, INTFileStore fileStore, object handle, QueryInformationLevel informationLevel) + { + result = null; + FileInformation fileInfo; + switch (informationLevel) + { + case QueryInformationLevel.SMB_INFO_QUERY_ALL_EAS: + { + result = null; + return NTStatus.STATUS_NOT_IMPLEMENTED; + } + case QueryInformationLevel.SMB_INFO_IS_NAME_VALID: + { + result = null; + return NTStatus.STATUS_NOT_IMPLEMENTED; + } + case QueryInformationLevel.SMB_QUERY_FILE_BASIC_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileBasicInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileBasicInformation fileBasicInfo = (FileBasicInformation)fileInfo; + + QueryFileBasicInfo information = new QueryFileBasicInfo(); + information.CreationTime = fileBasicInfo.CreationTime; + information.LastAccessTime = fileBasicInfo.LastAccessTime; + information.LastWriteTime = fileBasicInfo.LastWriteTime; + information.LastChangeTime = fileBasicInfo.LastWriteTime; + information.ExtFileAttributes = (ExtendedFileAttributes)fileBasicInfo.FileAttributes; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_STANDARD_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileStandardInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileStandardInformation fileStandardInfo = (FileStandardInformation)fileInfo; + + QueryFileStandardInfo information = new QueryFileStandardInfo(); + information.AllocationSize = fileStandardInfo.AllocationSize; + information.EndOfFile = fileStandardInfo.EndOfFile; + information.DeletePending = fileStandardInfo.DeletePending; + information.Directory = fileStandardInfo.Directory; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_EA_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileEaInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileEaInformation fileEAInfo = (FileEaInformation)fileInfo; + + QueryFileExtendedAttributeInfo information = new QueryFileExtendedAttributeInfo(); + information.EASize = fileEAInfo.EaSize; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_NAME_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileNameInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileNameInformation fileNameInfo = (FileNameInformation)fileInfo; + + QueryFileNameInfo information = new QueryFileNameInfo(); + information.FileName = fileNameInfo.FileName; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_ALL_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileAllInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileAllInformation fileAllInfo = (FileAllInformation)fileInfo; + + QueryFileAllInfo information = new QueryFileAllInfo(); + information.CreationDateTime = fileAllInfo.BasicInformation.CreationTime; + information.LastAccessDateTime = fileAllInfo.BasicInformation.LastAccessTime; + information.LastWriteDateTime = fileAllInfo.BasicInformation.LastWriteTime; + information.LastChangeTime = fileAllInfo.BasicInformation.LastWriteTime; + information.ExtFileAttributes = (ExtendedFileAttributes)fileAllInfo.BasicInformation.FileAttributes; + information.AllocationSize = fileAllInfo.StandardInformation.AllocationSize; + information.EndOfFile = fileAllInfo.StandardInformation.EndOfFile; + information.DeletePending = fileAllInfo.StandardInformation.DeletePending; + information.Directory = fileAllInfo.StandardInformation.Directory; + information.EASize = fileAllInfo.EaInformation.EaSize; + information.FileName = fileAllInfo.NameInformation.FileName; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_ALT_NAME_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileAlternateNameInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileAlternateNameInformation fileAltNameInfo = (FileAlternateNameInformation)fileInfo; + + QueryFileAltNameInfo information = new QueryFileAltNameInfo(); + information.FileName = fileAltNameInfo.FileName; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_STREAM_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileStreamInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileStreamInformation fileStreamInfo = (FileStreamInformation)fileInfo; + + QueryFileStreamInfo information = new QueryFileStreamInfo(); + information.StreamSize = fileStreamInfo.StreamSize; + information.StreamAllocationSize = fileStreamInfo.StreamAllocationSize; + information.StreamName = fileStreamInfo.StreamName; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryInformationLevel.SMB_QUERY_FILE_COMPRESSION_INFO: + { + NTStatus status = fileStore.GetFileInformation(out fileInfo, handle, FileInformationClass.FileCompressionInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileCompressionInformation fileCompressionInfo = (FileCompressionInformation)fileInfo; + + QueryFileCompressionInfo information = new QueryFileCompressionInfo(); + information.CompressedFileSize = fileCompressionInfo.CompressedFileSize; + information.CompressionFormat = fileCompressionInfo.CompressionFormat; + information.CompressionUnitShift = fileCompressionInfo.CompressionUnitShift; + information.ChunkShift = fileCompressionInfo.ChunkShift; + information.ClusterShift = fileCompressionInfo.ClusterShift; + information.Reserved = fileCompressionInfo.Reserved; + result = information; + return NTStatus.STATUS_SUCCESS; + } + default: + { + result = null; + return NTStatus.STATUS_OS2_INVALID_LEVEL; + } + } + } + } +} diff --git a/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryDirectory.cs b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryDirectory.cs new file mode 100644 index 0000000..f89aa4d --- /dev/null +++ b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryDirectory.cs @@ -0,0 +1,139 @@ +/* Copyright (C) 2014-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.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server.SMB1 +{ + public partial class SMB1FileStoreHelper + { + // Filename pattern examples: + // '\Directory' - Get the directory entry + // '\Directory\*' - List the directory files + // '\Directory\s*' - List the directory files starting with s (cmd.exe will use this syntax when entering 's' and hitting tab for autocomplete) + // '\Directory\<.inf' (Update driver will use this syntax) + // '\Directory\exefile"*' (cmd.exe will use this syntax when entering an exe without its extension, explorer will use this opening a directory from the run menu) + /// The filename pattern to search for. This field MAY contain wildcard characters + /// + public static NTStatus QueryDirectory(out List result, INTFileStore fileStore, string fileNamePattern, FileInformationClass fileInformation) + { + int separatorIndex = fileNamePattern.LastIndexOf('\\'); + if (separatorIndex >= 0) + { + string path = fileNamePattern.Substring(0, separatorIndex + 1); + string fileName = fileNamePattern.Substring(separatorIndex + 1); + object handle; + FileStatus fileStatus; + NTStatus createStatus = fileStore.CreateFile(out handle, out fileStatus, path, DirectoryAccessMask.FILE_LIST_DIRECTORY | DirectoryAccessMask.FILE_TRAVERSE, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE); + if (createStatus != NTStatus.STATUS_SUCCESS) + { + result = null; + return createStatus; + } + return fileStore.QueryDirectory(out result, handle, fileName, fileInformation); + } + else + { + result = null; + return NTStatus.STATUS_INVALID_PARAMETER; + } + } + + /// + public static FindInformationList GetFindInformationList(List entries, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys, int maxLength) + { + FindInformationList result = new FindInformationList(); + for (int index = 0; index < entries.Count; index++) + { + FindInformation infoEntry = GetFindInformation(entries[index], informationLevel, isUnicode, returnResumeKeys); + result.Add(infoEntry); + if (result.GetLength(isUnicode) > maxLength) + { + result.RemoveAt(result.Count - 1); + break; + } + } + return result; + } + + /// + public static FindInformation GetFindInformation(QueryDirectoryFileInformation entry, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) + { + switch (informationLevel) + { + case FindInformationLevel.SMB_FIND_FILE_DIRECTORY_INFO: + { + FileDirectoryInformation fileDirectoryInfo = (FileDirectoryInformation)entry; + + FindFileDirectoryInfo result = new FindFileDirectoryInfo(); + result.FileIndex = fileDirectoryInfo.FileIndex; + result.CreationTime = fileDirectoryInfo.CreationTime; + result.LastAccessTime = fileDirectoryInfo.LastAccessTime; + result.LastWriteTime = fileDirectoryInfo.LastWriteTime; + result.LastAttrChangeTime = fileDirectoryInfo.LastWriteTime; + result.EndOfFile = fileDirectoryInfo.EndOfFile; + result.AllocationSize = fileDirectoryInfo.AllocationSize; + result.ExtFileAttributes = (ExtendedFileAttributes)fileDirectoryInfo.FileAttributes; + result.FileName = fileDirectoryInfo.FileName; + return result; + } + case FindInformationLevel.SMB_FIND_FILE_FULL_DIRECTORY_INFO: + { + FileFullDirectoryInformation fileFullDirectoryInfo = (FileFullDirectoryInformation)entry; + + FindFileFullDirectoryInfo result = new FindFileFullDirectoryInfo(); + result.FileIndex = fileFullDirectoryInfo.FileIndex; + result.CreationTime = fileFullDirectoryInfo.CreationTime; + result.LastAccessTime = fileFullDirectoryInfo.LastAccessTime; + result.LastWriteTime = fileFullDirectoryInfo.LastWriteTime; + result.LastAttrChangeTime = fileFullDirectoryInfo.LastWriteTime; + result.EndOfFile = fileFullDirectoryInfo.EndOfFile; + result.AllocationSize = fileFullDirectoryInfo.AllocationSize; + result.ExtFileAttributes = (ExtendedFileAttributes)fileFullDirectoryInfo.FileAttributes; + result.EASize = fileFullDirectoryInfo.EaSize; + result.FileName = fileFullDirectoryInfo.FileName; + return result; + } + case FindInformationLevel.SMB_FIND_FILE_NAMES_INFO: + { + FileNamesInformation fileNamesInfo = (FileNamesInformation)entry; + + FindFileNamesInfo result = new FindFileNamesInfo(); + result.FileIndex = fileNamesInfo.FileIndex; + result.FileName = fileNamesInfo.FileName; + return result; + } + case FindInformationLevel.SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + { + FileBothDirectoryInformation fileBothDirectoryInfo = (FileBothDirectoryInformation)entry; + + FindFileBothDirectoryInfo result = new FindFileBothDirectoryInfo(); + result.FileIndex = fileBothDirectoryInfo.FileIndex; + result.CreationTime = fileBothDirectoryInfo.CreationTime; + result.LastAccessTime = fileBothDirectoryInfo.LastAccessTime; + result.LastWriteTime = fileBothDirectoryInfo.LastWriteTime; + result.LastChangeTime = fileBothDirectoryInfo.LastWriteTime; + result.EndOfFile = fileBothDirectoryInfo.EndOfFile; + result.AllocationSize = fileBothDirectoryInfo.AllocationSize; + result.ExtFileAttributes = (ExtendedFileAttributes)fileBothDirectoryInfo.FileAttributes; + result.EASize = fileBothDirectoryInfo.EaSize; + result.Reserved = fileBothDirectoryInfo.Reserved; + result.ShortName = fileBothDirectoryInfo.ShortName; + result.FileName = fileBothDirectoryInfo.FileName; + return result; + } + default: + { + throw new UnsupportedInformationLevelException(); + } + } + } + } +} diff --git a/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryFileSystem.cs b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryFileSystem.cs new file mode 100644 index 0000000..f11eb0e --- /dev/null +++ b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.QueryFileSystem.cs @@ -0,0 +1,99 @@ +/* Copyright (C) 2014-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.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server.SMB1 +{ + public partial class SMB1FileStoreHelper + { + public static NTStatus GetFileSystemInformation(out QueryFSInformation result, INTFileStore fileStore, QueryFSInformationLevel informationLevel) + { + result = null; + + FileSystemInformation fsInfo; + switch (informationLevel) + { + case QueryFSInformationLevel.SMB_QUERY_FS_VOLUME_INFO: + { + NTStatus status = fileStore.GetFileSystemInformation(out fsInfo, FileSystemInformationClass.FileFsVolumeInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileFsVolumeInformation volumeInfo = (FileFsVolumeInformation)fsInfo; + + QueryFSVolumeInfo information = new QueryFSVolumeInfo(); + information.VolumeCreationTime = volumeInfo.VolumeCreationTime; + information.SerialNumber = volumeInfo.VolumeSerialNumber; + information.VolumeLabel = volumeInfo.VolumeLabel; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryFSInformationLevel.SMB_QUERY_FS_SIZE_INFO: + { + NTStatus status = fileStore.GetFileSystemInformation(out fsInfo, FileSystemInformationClass.FileFsSizeInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileFsSizeInformation fsSizeInfo = (FileFsSizeInformation)fsInfo; + + QueryFSSizeInfo information = new QueryFSSizeInfo(); + information.TotalAllocationUnits = fsSizeInfo.TotalAllocationUnits; + information.TotalFreeAllocationUnits = fsSizeInfo.AvailableAllocationUnits; + information.BytesPerSector = fsSizeInfo.BytesPerSector; + information.SectorsPerAllocationUnit = fsSizeInfo.SectorsPerAllocationUnit; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryFSInformationLevel.SMB_QUERY_FS_DEVICE_INFO: + { + NTStatus status = fileStore.GetFileSystemInformation(out fsInfo, FileSystemInformationClass.FileFsDeviceInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileFsDeviceInformation fsDeviceInfo = (FileFsDeviceInformation)fsInfo; + + QueryFSDeviceInfo information = new QueryFSDeviceInfo(); + information.DeviceType = fsDeviceInfo.DeviceType; + information.DeviceCharacteristics = fsDeviceInfo.Characteristics; + result = information; + return NTStatus.STATUS_SUCCESS; + } + case QueryFSInformationLevel.SMB_QUERY_FS_ATTRIBUTE_INFO: + { + NTStatus status = fileStore.GetFileSystemInformation(out fsInfo, FileSystemInformationClass.FileFsAttributeInformation); + if (status != NTStatus.STATUS_SUCCESS) + { + return status; + } + + FileFsAttributeInformation fsAttributeInfo = (FileFsAttributeInformation)fsInfo; + + QueryFSAttibuteInfo information = new QueryFSAttibuteInfo(); + information.FileSystemAttributes = fsAttributeInfo.FileSystemAttributes; + information.MaxFileNameLengthInBytes = fsAttributeInfo.MaximumComponentNameLength; + information.FileSystemName = fsAttributeInfo.FileSystemName; + result = information; + return NTStatus.STATUS_SUCCESS; + } + default: + { + return NTStatus.STATUS_OS2_INVALID_LEVEL; + } + } + } + } +} diff --git a/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Set.cs b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Set.cs new file mode 100644 index 0000000..5eab2c3 --- /dev/null +++ b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.Set.cs @@ -0,0 +1,58 @@ +/* Copyright (C) 2014-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 SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server.SMB1 +{ + public partial class SMB1FileStoreHelper + { + public static NTStatus SetFileInformation(INTFileStore fileStore, object handle, SetInformation information) + { + if (information is SetFileBasicInfo) + { + SetFileBasicInfo basicInfo = (SetFileBasicInfo)information; + FileBasicInformation fileBasicInfo = new FileBasicInformation(); + fileBasicInfo.CreationTime = basicInfo.CreationTime; + fileBasicInfo.LastAccessTime = basicInfo.LastAccessTime; + fileBasicInfo.LastWriteTime = basicInfo.LastWriteTime; + fileBasicInfo.ChangeTime = basicInfo.LastChangeTime; + fileBasicInfo.FileAttributes = (FileAttributes)basicInfo.ExtFileAttributes; + fileBasicInfo.Reserved = basicInfo.Reserved; + return fileStore.SetFileInformation(handle, fileBasicInfo); + } + else if (information is SetFileDispositionInfo) + { + FileDispositionInformation fileDispositionInfo = new FileDispositionInformation(); + fileDispositionInfo.DeletePending = ((SetFileDispositionInfo)information).DeletePending; + return fileStore.SetFileInformation(handle, fileDispositionInfo); + } + else if (information is SetFileAllocationInfo) + { + // This information level is used to set the file length in bytes. + // Note: the input will NOT be a multiple of the cluster size / bytes per sector. + FileAllocationInformation fileAllocationInfo = new FileAllocationInformation(); + fileAllocationInfo.AllocationSize = ((SetFileAllocationInfo)information).AllocationSize; + return fileStore.SetFileInformation(handle, fileAllocationInfo); + } + else if (information is SetFileEndOfFileInfo) + { + FileEndOfFileInformation fileEndOfFileInfo = new FileEndOfFileInformation(); + fileEndOfFileInfo.EndOfFile = ((SetFileEndOfFileInfo)information).EndOfFile; + return fileStore.SetFileInformation(handle, fileEndOfFileInfo); + } + else + { + return NTStatus.STATUS_NOT_IMPLEMENTED; + } + } + } +} diff --git a/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.cs b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.cs new file mode 100644 index 0000000..8b3edcf --- /dev/null +++ b/SMBLibrary/Server/SMB1/SMB1FileStoreHelper.cs @@ -0,0 +1,184 @@ +/* Copyright (C) 2014-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.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server.SMB1 +{ + public partial class SMB1FileStoreHelper + { + public static NTStatus CreateDirectory(INTFileStore fileStore, string path) + { + object handle; + FileStatus fileStatus; + NTStatus createStatus = fileStore.CreateFile(out handle, out fileStatus, path, DirectoryAccessMask.FILE_ADD_SUBDIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_CREATE, CreateOptions.FILE_DIRECTORY_FILE); + if (createStatus != NTStatus.STATUS_SUCCESS) + { + return createStatus; + } + fileStore.CloseFile(handle); + return createStatus; + } + + public static NTStatus DeleteDirectory(INTFileStore fileStore, string path) + { + return Delete(fileStore, path, CreateOptions.FILE_DIRECTORY_FILE); + } + + public static NTStatus DeleteFile(INTFileStore fileStore, string path) + { + return Delete(fileStore, path, CreateOptions.FILE_NON_DIRECTORY_FILE); + } + + public static NTStatus Delete(INTFileStore fileStore, string path, CreateOptions createOptions) + { + object handle; + FileStatus fileStatus; + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, path, DirectoryAccessMask.DELETE, 0, CreateDisposition.FILE_OPEN, createOptions); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + return openStatus; + } + FileDispositionInformation fileDispositionInfo = new FileDispositionInformation(); + fileDispositionInfo.DeletePending = true; + NTStatus setStatus = fileStore.SetFileInformation(handle, fileDispositionInfo); + if (setStatus != NTStatus.STATUS_SUCCESS) + { + return setStatus; + } + NTStatus closeStatus = fileStore.CloseFile(handle); + return closeStatus; + } + + public static NTStatus Rename(INTFileStore fileStore, string oldName, string newName, SMBFileAttributes searchAttributes) + { + object handle; + FileStatus fileStatus; + CreateOptions createOptions = 0; + if (searchAttributes == SMBFileAttributes.Normal) + { + createOptions = CreateOptions.FILE_NON_DIRECTORY_FILE; + } + else if ((searchAttributes & SMBFileAttributes.Directory) > 0) + { + createOptions = CreateOptions.FILE_DIRECTORY_FILE; + } + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, oldName, DirectoryAccessMask.DELETE, 0, CreateDisposition.FILE_OPEN, createOptions); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + return openStatus; + } + FileRenameInformationType2 renameInfo = new FileRenameInformationType2(); + renameInfo.ReplaceIfExists = false; + renameInfo.FileName = newName; + NTStatus setStatus = fileStore.SetFileInformation(handle, renameInfo); + if (setStatus != NTStatus.STATUS_SUCCESS) + { + return setStatus; + } + NTStatus closeStatus = fileStore.CloseFile(handle); + return closeStatus; + } + + public static NTStatus CheckDirectory(INTFileStore fileStore, string path) + { + object handle; + FileStatus fileStatus; + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, path, (AccessMask)0, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + return openStatus; + } + + fileStore.CloseFile(handle); + return NTStatus.STATUS_SUCCESS; + } + + public static NTStatus QueryInformation(out FileNetworkOpenInformation fileInfo, INTFileStore fileStore, string path) + { + object handle; + FileStatus fileStatus; + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, path, FileAccessMask.FILE_READ_ATTRIBUTES, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, 0); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + fileInfo = null; + return openStatus; + } + + fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(fileStore, handle); + return NTStatus.STATUS_SUCCESS; + } + + public static NTStatus SetInformation(INTFileStore fileStore, string path, SMBFileAttributes fileAttributes, DateTime? lastWriteTime) + { + object handle; + FileStatus fileStatus; + NTStatus openStatus = fileStore.CreateFile(out handle, out fileStatus, path, FileAccessMask.FILE_WRITE_ATTRIBUTES, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, 0); + if (openStatus != NTStatus.STATUS_SUCCESS) + { + return openStatus; + } + + FileBasicInformation basicInfo = new FileBasicInformation(); + basicInfo.LastWriteTime = lastWriteTime; + + if ((fileAttributes & SMBFileAttributes.Hidden) > 0) + { + basicInfo.FileAttributes |= FileAttributes.Hidden; + } + + if ((fileAttributes & SMBFileAttributes.ReadOnly) > 0) + { + basicInfo.FileAttributes |= FileAttributes.ReadOnly; + } + + if ((fileAttributes & SMBFileAttributes.Archive) > 0) + { + basicInfo.FileAttributes |= FileAttributes.Archive; + } + + return fileStore.SetFileInformation(handle, basicInfo); + } + + public static NTStatus SetInformation2(INTFileStore fileStore, object handle, DateTime? creationTime, DateTime? lastAccessTime, DateTime? lastWriteTime) + { + FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(fileStore, handle); + FileBasicInformation basicInfo = new FileBasicInformation(); + basicInfo.FileAttributes = fileInfo.FileAttributes; + basicInfo.CreationTime = creationTime; + basicInfo.LastAccessTime = lastAccessTime; + basicInfo.LastWriteTime = lastWriteTime; + return fileStore.SetFileInformation(handle, basicInfo); + } + + public static SMBFileAttributes GetFileAttributes(FileAttributes attributes) + { + SMBFileAttributes result = SMBFileAttributes.Normal; + if ((attributes & FileAttributes.Hidden) > 0) + { + result |= SMBFileAttributes.Hidden; + } + if ((attributes & FileAttributes.ReadOnly) > 0) + { + result |= SMBFileAttributes.ReadOnly; + } + if ((attributes & FileAttributes.Archive) > 0) + { + result |= SMBFileAttributes.Archive; + } + if ((attributes & FileAttributes.Directory) > 0) + { + result |= SMBFileAttributes.Directory; + } + + return result; + } + } +} diff --git a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Find.cs b/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Find.cs deleted file mode 100644 index 9c23ade..0000000 --- a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Find.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2014-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.Text; -using SMBLibrary.SMB1; -using Utilities; - -namespace SMBLibrary.Server.SMB1 -{ - public partial class SMB1FileSystemHelper - { - // Filename pattern examples: - // '\Directory' - Get the directory entry - // '\Directory\*' - List the directory files - // '\Directory\s*' - List the directory files starting with s (cmd.exe will use this syntax when entering 's' and hitting tab for autocomplete) - // '\Directory\<.inf' (Update driver will use this syntax) - // '\Directory\exefile"*' (cmd.exe will use this syntax when entering an exe without its extension, explorer will use this opening a directory from the run menu) - /// The filename pattern to search for. This field MAY contain wildcard characters - /// - public static NTStatus FindEntries(out List entries, IFileSystem fileSystem, string fileNamePattern) - { - int separatorIndex = fileNamePattern.LastIndexOf('\\'); - if (separatorIndex >= 0) - { - string path = fileNamePattern.Substring(0, separatorIndex + 1); - string fileName = fileNamePattern.Substring(separatorIndex + 1); - return NTFileSystemHelper.FindEntries(out entries, fileSystem, path, fileName); - } - else - { - entries = null; - return NTStatus.STATUS_INVALID_PARAMETER; - } - } - - /// - public static FindInformationList GetFindInformationList(List entries, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys, int maxLength) - { - FindInformationList result = new FindInformationList(); - for (int index = 0; index < entries.Count; index++) - { - FindInformation infoEntry = GetFindInformation(entries[index], informationLevel, isUnicode, returnResumeKeys); - result.Add(infoEntry); - if (result.GetLength(isUnicode) > maxLength) - { - result.RemoveAt(result.Count - 1); - break; - } - } - return result; - } - - /// - public static FindInformation GetFindInformation(FileSystemEntry entry, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) - { - switch (informationLevel) - { - case FindInformationLevel.SMB_FIND_FILE_DIRECTORY_INFO: - { - FindFileDirectoryInfo result = new FindFileDirectoryInfo(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.LastAttrChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.ExtFileAttributes = GetExtendedFileAttributes(entry); - result.FileName = entry.Name; - return result; - } - case FindInformationLevel.SMB_FIND_FILE_FULL_DIRECTORY_INFO: - { - FindFileFullDirectoryInfo result = new FindFileFullDirectoryInfo(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.LastAttrChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.ExtFileAttributes = GetExtendedFileAttributes(entry); - result.FileName = entry.Name; - return result; - } - case FindInformationLevel.SMB_FIND_FILE_NAMES_INFO: - { - FindFileNamesInfo result = new FindFileNamesInfo(); - result.FileName = entry.Name; - return result; - } - case FindInformationLevel.SMB_FIND_FILE_BOTH_DIRECTORY_INFO: - { - FindFileBothDirectoryInfo result = new FindFileBothDirectoryInfo(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.LastChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.ExtFileAttributes = GetExtendedFileAttributes(entry); - result.ShortName = NTFileSystemHelper.GetShortName(entry.Name); - result.FileName = entry.Name; - return result; - } - default: - { - throw new UnsupportedInformationLevelException(); - } - } - } - } -} diff --git a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Query.cs b/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Query.cs deleted file mode 100644 index 550ee58..0000000 --- a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Query.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright (C) 2014-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.Text; -using SMBLibrary.SMB1; -using Utilities; - -namespace SMBLibrary.Server.SMB1 -{ - public partial class SMB1FileSystemHelper - { - public static NTStatus GetFileInformation(out QueryInformation result, FileSystemEntry entry, bool deletePending, QueryInformationLevel informationLevel) - { - switch (informationLevel) - { - case QueryInformationLevel.SMB_QUERY_FILE_BASIC_INFO: - { - QueryFileBasicInfo information = new QueryFileBasicInfo(); - information.CreationTime = entry.CreationTime; - information.LastAccessTime = entry.LastAccessTime; - information.LastWriteTime = entry.LastWriteTime; - information.LastChangeTime = entry.LastWriteTime; - information.ExtFileAttributes = GetExtendedFileAttributes(entry); - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_STANDARD_INFO: - { - QueryFileStandardInfo information = new QueryFileStandardInfo(); - information.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - information.EndOfFile = (long)entry.Size; - information.DeletePending = deletePending; - information.Directory = entry.IsDirectory; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_EA_INFO: - { - QueryFileExtendedAttributeInfo information = new QueryFileExtendedAttributeInfo(); - information.EASize = 0; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_NAME_INFO: - { - QueryFileNameInfo information = new QueryFileNameInfo(); - information.FileName = entry.Name; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_ALL_INFO: - { - QueryFileAllInfo information = new QueryFileAllInfo(); - information.CreationDateTime = entry.CreationTime; - information.LastAccessDateTime = entry.LastAccessTime; - information.LastWriteDateTime = entry.LastWriteTime; - information.ExtFileAttributes = GetExtendedFileAttributes(entry); - information.LastChangeTime = entry.LastWriteTime; - information.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - information.EndOfFile = (long)entry.Size; - information.DeletePending = deletePending; - information.Directory = entry.IsDirectory; - information.EASize = 0; - information.FileName = entry.Name; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_ALT_NAME_INFO: - { - QueryFileAltNameInfo information = new QueryFileAltNameInfo(); - information.FileName = NTFileSystemHelper.GetShortName(entry.Name); - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_STREAM_INFO: - { - QueryFileStreamInfo information = new QueryFileStreamInfo(); - information.StreamSize = (long)entry.Size; - information.StreamAllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - information.StreamName = "::$DATA"; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryInformationLevel.SMB_QUERY_FILE_COMPRESSION_INFO: - { - QueryFileCompressionInfo information = new QueryFileCompressionInfo(); - information.CompressionFormat = CompressionFormat.COMPRESSION_FORMAT_NONE; - result = information; - return NTStatus.STATUS_SUCCESS; - } - default: - { - result = null; - return NTStatus.STATUS_OS2_INVALID_LEVEL; - } - } - } - - public static SMBFileAttributes GetFileAttributes(FileSystemEntry entry) - { - SMBFileAttributes attributes = SMBFileAttributes.Normal; - if (entry.IsHidden) - { - attributes |= SMBFileAttributes.Hidden; - } - if (entry.IsReadonly) - { - attributes |= SMBFileAttributes.ReadOnly; - } - if (entry.IsArchived) - { - attributes |= SMBFileAttributes.Archive; - } - if (entry.IsDirectory) - { - attributes |= SMBFileAttributes.Directory; - } - - return attributes; - } - - public static ExtendedFileAttributes GetExtendedFileAttributes(FileSystemEntry entry) - { - ExtendedFileAttributes attributes = 0; - if (entry.IsHidden) - { - attributes |= ExtendedFileAttributes.Hidden; - } - if (entry.IsReadonly) - { - attributes |= ExtendedFileAttributes.Readonly; - } - if (entry.IsArchived) - { - attributes |= ExtendedFileAttributes.Archive; - } - if (entry.IsDirectory) - { - attributes |= ExtendedFileAttributes.Directory; - } - - if ((uint)attributes == 0) - { - attributes = ExtendedFileAttributes.Normal; - } - - return attributes; - } - } -} diff --git a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.QueryFileSystem.cs b/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.QueryFileSystem.cs deleted file mode 100644 index bb2024b..0000000 --- a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.QueryFileSystem.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (C) 2014-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.Text; -using SMBLibrary.SMB1; -using Utilities; - -namespace SMBLibrary.Server.SMB1 -{ - public partial class SMB1FileSystemHelper - { - public static NTStatus GetFileSystemInformation(out QueryFSInformation result, QueryFSInformationLevel informationLevel, IFileSystem fileSystem) - { - switch (informationLevel) - { - case QueryFSInformationLevel.SMB_QUERY_FS_VOLUME_INFO: - { - QueryFSVolumeInfo information = new QueryFSVolumeInfo(); - information.VolumeCreationTime = DateTime.Now; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryFSInformationLevel.SMB_QUERY_FS_SIZE_INFO: - { - QueryFSSizeInfo information = new QueryFSSizeInfo(); - information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize; - information.TotalFreeAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; - information.BytesPerSector = NTFileSystemHelper.BytesPerSector; - information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryFSInformationLevel.SMB_QUERY_FS_DEVICE_INFO: - { - QueryFSDeviceInfo information = new QueryFSDeviceInfo(); - information.DeviceCharacteristics = DeviceCharacteristics.IsMounted; - information.DeviceType = DeviceType.Disk; - result = information; - return NTStatus.STATUS_SUCCESS; - } - case QueryFSInformationLevel.SMB_QUERY_FS_ATTRIBUTE_INFO: - { - QueryFSAttibuteInfo information = new QueryFSAttibuteInfo(); - information.FileSystemAttributes = FileSystemAttributes.UnicodeOnDisk; - information.MaxFileNameLengthInBytes = 255; - information.FileSystemName = fileSystem.Name; - result = information; - return NTStatus.STATUS_SUCCESS; - } - default: - { - result = null; - return NTStatus.STATUS_OS2_INVALID_LEVEL; - } - } - } - } -} diff --git a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Set.cs b/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Set.cs deleted file mode 100644 index 895c0eb..0000000 --- a/SMBLibrary/Server/SMB1/SMB1FileSystemHelper.Set.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* Copyright (C) 2014-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 SMBLibrary.SMB1; -using Utilities; - -namespace SMBLibrary.Server.SMB1 -{ - public partial class SMB1FileSystemHelper - { - public static NTStatus SetFileInformation(IFileSystem fileSystem, OpenFileObject openFile, SetInformation information, ConnectionState state) - { - if (information is SetFileBasicInfo) - { - SetFileBasicInfo basicInfo = (SetFileBasicInfo)information; - bool isHidden = (basicInfo.ExtFileAttributes & ExtendedFileAttributes.Hidden) > 0; - bool isReadonly = (basicInfo.ExtFileAttributes & ExtendedFileAttributes.Readonly) > 0; - bool isArchived = (basicInfo.ExtFileAttributes & ExtendedFileAttributes.Archive) > 0; - try - { - fileSystem.SetAttributes(openFile.Path, isHidden, isReadonly, isArchived); - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file attributes on '{0}'. Access Denied.", openFile.Path); - return NTStatus.STATUS_ACCESS_DENIED; - } - - try - { - fileSystem.SetDates(openFile.Path, basicInfo.CreationTime, basicInfo.LastWriteTime, basicInfo.LastAccessTime); - } - catch (IOException ex) - { - ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); - if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) - { - // Returning STATUS_SHARING_VIOLATION is undocumented but apparently valid - state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Sharing Violation.", openFile.Path); - return NTStatus.STATUS_SHARING_VIOLATION; - } - else - { - state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Data Error.", openFile.Path); - return NTStatus.STATUS_DATA_ERROR; - } - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Access Denied.", openFile.Path); - return NTStatus.STATUS_ACCESS_DENIED; - } - return NTStatus.STATUS_SUCCESS; - } - else if (information is SetFileDispositionInfo) - { - if (((SetFileDispositionInfo)information).DeletePending) - { - // We're supposed to delete the file on close, but it's too late to report errors at this late stage - if (openFile.Stream != null) - { - openFile.Stream.Close(); - } - - try - { - state.LogToServer(Severity.Information, "SetFileInformation: Deleting file '{0}'", openFile.Path); - fileSystem.Delete(openFile.Path); - } - catch (IOException ex) - { - ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); - if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) - { - state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}'. Sharing Violation.", openFile.Path); - return NTStatus.STATUS_SHARING_VIOLATION; - } - else - { - state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}'. Data Error.", openFile.Path); - return NTStatus.STATUS_DATA_ERROR; - } - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}', Access Denied.", openFile.Path); - return NTStatus.STATUS_ACCESS_DENIED; - } - } - return NTStatus.STATUS_SUCCESS; - } - else if (information is SetFileAllocationInfo) - { - // This information level is used to set the file length in bytes. - // Note: the input will NOT be a multiple of the cluster size / bytes per sector. - long allocationSize = ((SetFileAllocationInfo)information).AllocationSize; - try - { - openFile.Stream.SetLength(allocationSize); - } - catch (IOException ex) - { - ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); - if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) - { - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Sharing Violation.", openFile.Path); - return NTStatus.STATUS_SHARING_VIOLATION; - } - else - { - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Data Error.", openFile.Path); - return NTStatus.STATUS_DATA_ERROR; - } - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Access Denied.", openFile.Path); - return NTStatus.STATUS_ACCESS_DENIED; - } - return NTStatus.STATUS_SUCCESS; - } - else if (information is SetFileEndOfFileInfo) - { - long endOfFile = ((SetFileEndOfFileInfo)information).EndOfFile; - try - { - openFile.Stream.SetLength(endOfFile); - } - catch (IOException ex) - { - ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); - if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) - { - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Sharing Violation.", openFile.Path); - return NTStatus.STATUS_SHARING_VIOLATION; - } - else - { - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Data Error.", openFile.Path); - return NTStatus.STATUS_DATA_ERROR; - } - } - catch (UnauthorizedAccessException) - { - state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Access Denied.", openFile.Path); - return NTStatus.STATUS_ACCESS_DENIED; - } - return NTStatus.STATUS_SUCCESS; - } - else - { - return NTStatus.STATUS_NOT_IMPLEMENTED; - } - } - } -} diff --git a/SMBLibrary/Server/SMB1/ServerResponseHelper.cs b/SMBLibrary/Server/SMB1/ServerResponseHelper.cs index 028562d..e82a5be 100644 --- a/SMBLibrary/Server/SMB1/ServerResponseHelper.cs +++ b/SMBLibrary/Server/SMB1/ServerResponseHelper.cs @@ -26,20 +26,14 @@ namespace SMBLibrary.Server.SMB1 } state.LogToServer(Severity.Verbose, "Close: Closing file '{0}'", openFile.Path); - session.RemoveOpenFile(request.FID); - if (openFile.DeleteOnClose && share is FileSystemShare) + header.Status = share.FileStore.CloseFile(openFile.Handle); + if (header.Status != NTStatus.STATUS_SUCCESS) { - try - { - ((FileSystemShare)share).FileSystem.Delete(openFile.Path); - } - catch - { - state.LogToServer(Severity.Debug, "Close: Cannot delete '{0}'", openFile.Path); - } + return new ErrorResponse(request.CommandName); } - CloseResponse response = new CloseResponse(); - return response; + + session.RemoveOpenFile(request.FID); + return new CloseResponse(); } internal static SMB1Command GetFindClose2Request(SMB1Header header, FindClose2Request request, SMB1ConnectionState state) diff --git a/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs b/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs index 3f3aa73..3aaa413 100644 --- a/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs +++ b/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs @@ -18,11 +18,21 @@ namespace SMBLibrary.Server.SMB1 internal static Transaction2FindFirst2Response GetSubcommandResponse(SMB1Header header, Transaction2FindFirst2Request subcommand, FileSystemShare share, SMB1ConnectionState state) { SMB1Session session = state.GetSession(header.UID); - IFileSystem fileSystem = share.FileSystem; string fileNamePattern = subcommand.FileName; - List entries; - NTStatus searchStatus = SMB1FileSystemHelper.FindEntries(out entries, fileSystem, fileNamePattern); + List entries; + FileInformationClass informationClass; + try + { + informationClass = GetFileInformationClass(subcommand.InformationLevel); + } + catch (UnsupportedInformationLevelException) + { + header.Status = NTStatus.STATUS_OS2_INVALID_LEVEL; + return null; + } + + NTStatus searchStatus = SMB1FileStoreHelper.QueryDirectory(out entries, share.FileStore, fileNamePattern, informationClass); if (searchStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', NTStatus: {1}", fileNamePattern, searchStatus.ToString()); @@ -41,18 +51,9 @@ namespace SMBLibrary.Server.SMB1 bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0; int entriesToReturn = Math.Min(subcommand.SearchCount, entries.Count); - List segment = entries.GetRange(0, entriesToReturn); + List segment = entries.GetRange(0, entriesToReturn); int maxLength = (int)state.GetMaxDataCount(header.PID).Value; - FindInformationList findInformationList; - try - { - findInformationList = SMB1FileSystemHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength); - } - catch (UnsupportedInformationLevelException) - { - header.Status = NTStatus.STATUS_OS2_INVALID_LEVEL; - return null; - } + FindInformationList findInformationList = SMB1FileStoreHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength); int returnCount = findInformationList.Count; Transaction2FindFirst2Response response = new Transaction2FindFirst2Response(); response.SetFindInformationList(findInformationList, header.UnicodeFlag); @@ -91,11 +92,11 @@ namespace SMBLibrary.Server.SMB1 bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0; int maxLength = (int)state.GetMaxDataCount(header.PID).Value; int maxCount = Math.Min(openSearch.Entries.Count - openSearch.EnumerationLocation, subcommand.SearchCount); - List segment = openSearch.Entries.GetRange(openSearch.EnumerationLocation, maxCount); + List segment = openSearch.Entries.GetRange(openSearch.EnumerationLocation, maxCount); FindInformationList findInformationList; try { - findInformationList = SMB1FileSystemHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength); + findInformationList = SMB1FileStoreHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength); } catch (UnsupportedInformationLevelException) { @@ -125,7 +126,7 @@ namespace SMBLibrary.Server.SMB1 Transaction2QueryFSInformationResponse response = new Transaction2QueryFSInformationResponse(); QueryFSInformation queryFSInformation; - NTStatus queryStatus = SMB1FileSystemHelper.GetFileSystemInformation(out queryFSInformation, subcommand.InformationLevel, share.FileSystem); + NTStatus queryStatus = SMB1FileStoreHelper.GetFileSystemInformation(out queryFSInformation, share.FileStore, subcommand.InformationLevel); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information level: {0}, NTStatus: {1}", subcommand.InformationLevel, queryStatus); @@ -145,20 +146,9 @@ namespace SMBLibrary.Server.SMB1 header.Status = NTStatus.STATUS_ACCESS_DENIED; return null; } - - IFileSystem fileSystem = share.FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(path); - if (entry == null) - { - // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND - // Returning STATUS_NO_SUCH_FILE caused an issue when executing ImageX.exe from WinPE 3.0 (32-bit) - state.LogToServer(Severity.Debug, "Transaction2QueryPathInformation: File not found, Path: '{0}'", path); - header.Status = NTStatus.STATUS_OBJECT_NAME_NOT_FOUND; - return null; - } Transaction2QueryPathInformationResponse response = new Transaction2QueryPathInformationResponse(); QueryInformation queryInformation; - NTStatus queryStatus = SMB1FileSystemHelper.GetFileInformation(out queryInformation, entry, false, subcommand.InformationLevel); + NTStatus queryStatus = SMB1FileStoreHelper.GetFileInformation(out queryInformation, share.FileStore, path, subcommand.InformationLevel); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information level: {1}, NTStatus: {2}", path, subcommand.InformationLevel, queryStatus); @@ -172,7 +162,6 @@ namespace SMBLibrary.Server.SMB1 internal static Transaction2QueryFileInformationResponse GetSubcommandResponse(SMB1Header header, Transaction2QueryFileInformationRequest subcommand, FileSystemShare share, SMB1ConnectionState state) { SMB1Session session = state.GetSession(header.UID); - IFileSystem fileSystem = share.FileSystem; OpenFileObject openFile = session.GetOpenFileObject(subcommand.FID); if (openFile == null) { @@ -186,15 +175,9 @@ namespace SMBLibrary.Server.SMB1 return null; } - FileSystemEntry entry = fileSystem.GetEntry(openFile.Path); - if (entry == null) - { - header.Status = NTStatus.STATUS_NO_SUCH_FILE; - return null; - } Transaction2QueryFileInformationResponse response = new Transaction2QueryFileInformationResponse(); QueryInformation queryInformation; - NTStatus queryStatus = SMB1FileSystemHelper.GetFileInformation(out queryInformation, entry, openFile.DeleteOnClose, subcommand.InformationLevel); + NTStatus queryStatus = SMB1FileStoreHelper.GetFileInformation(out queryInformation, share.FileStore, openFile.Handle, subcommand.InformationLevel); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information level: {1}, NTStatus: {2}", openFile.Path, subcommand.InformationLevel, queryStatus); @@ -237,7 +220,7 @@ namespace SMBLibrary.Server.SMB1 return null; } - NTStatus status = SMB1FileSystemHelper.SetFileInformation(share.FileSystem, openFile, information, state); + NTStatus status = SMB1FileStoreHelper.SetFileInformation(share.FileStore, openFile.Handle, information); if (status != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}' failed. Information level: {1}, NTStatus: {2}", openFile.Path, information.InformationLevel, status); @@ -247,5 +230,28 @@ namespace SMBLibrary.Server.SMB1 Transaction2SetFileInformationResponse response = new Transaction2SetFileInformationResponse(); return response; } + + private static FileInformationClass GetFileInformationClass(FindInformationLevel informationLevel) + { + switch (informationLevel) + { + case FindInformationLevel.SMB_INFO_STANDARD: + return FileInformationClass.FileDirectoryInformation; + case FindInformationLevel.SMB_INFO_QUERY_EA_SIZE: + return FileInformationClass.FileFullDirectoryInformation; + case FindInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST: + return FileInformationClass.FileDirectoryInformation; + case FindInformationLevel.SMB_FIND_FILE_DIRECTORY_INFO: + return FileInformationClass.FileDirectoryInformation; + case FindInformationLevel.SMB_FIND_FILE_FULL_DIRECTORY_INFO: + return FileInformationClass.FileFullDirectoryInformation; + case FindInformationLevel.SMB_FIND_FILE_NAMES_INFO: + return FileInformationClass.FileNamesInformation; + case FindInformationLevel.SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + return FileInformationClass.FileBothDirectoryInformation; + default: + throw new UnsupportedInformationLevelException(); + } + } } } diff --git a/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs b/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs index 070bbfe..fecd1f1 100644 --- a/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs +++ b/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs @@ -27,8 +27,18 @@ namespace SMBLibrary.Server.SMB1 } TransactionTransactNamedPipeResponse response = new TransactionTransactNamedPipeResponse(); - openFile.Stream.Write(subcommand.WriteData, 0, subcommand.WriteData.Length); - response.ReadData = ByteReader.ReadAllBytes(openFile.Stream); + int numberOfBytesWritten; + header.Status = share.FileStore.WriteFile(out numberOfBytesWritten, openFile.Handle, 0, subcommand.WriteData); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return null; + } + int maxCount = UInt16.MaxValue; + header.Status = share.FileStore.ReadFile(out response.ReadData, openFile.Handle, 0, maxCount); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return null; + } return response; } } diff --git a/SMBLibrary/Server/SMB2/CloseHelper.cs b/SMBLibrary/Server/SMB2/CloseHelper.cs index e81fc7d..465509a 100644 --- a/SMBLibrary/Server/SMB2/CloseHelper.cs +++ b/SMBLibrary/Server/SMB2/CloseHelper.cs @@ -22,29 +22,21 @@ namespace SMBLibrary.Server.SMB2 { return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED); } - string path = openFile.Path; + share.FileStore.CloseFile(openFile.Handle); session.RemoveOpenFile(request.FileId.Persistent); CloseResponse response = new CloseResponse(); if (request.PostQueryAttributes) { - if (share is NamedPipeShare) + FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, openFile.Path); + if (fileInfo != null) { - response.FileAttributes = FileAttributes.Temporary; - } - else // FileSystemShare - { - IFileSystem fileSystem = ((FileSystemShare)share).FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(path); - if (entry != null) - { - response.CreationTime = entry.CreationTime; - response.LastAccessTime = entry.LastAccessTime; - response.LastWriteTime = entry.LastWriteTime; - response.ChangeTime = entry.LastWriteTime; - response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - response.EndofFile = (long)entry.Size; - response.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry); - } + response.CreationTime = fileInfo.CreationTime; + response.LastAccessTime = fileInfo.LastAccessTime; + response.LastWriteTime = fileInfo.LastWriteTime; + response.ChangeTime = fileInfo.ChangeTime; + response.AllocationSize = fileInfo.AllocationSize; + response.EndofFile = fileInfo.EndOfFile; + response.FileAttributes = fileInfo.FileAttributes; } } return response; diff --git a/SMBLibrary/Server/SMB2/CreateHelper.cs b/SMBLibrary/Server/SMB2/CreateHelper.cs index ade1115..a45d869 100644 --- a/SMBLibrary/Server/SMB2/CreateHelper.cs +++ b/SMBLibrary/Server/SMB2/CreateHelper.cs @@ -32,48 +32,29 @@ namespace SMBLibrary.Server.SMB2 } } + object handle; + FileStatus fileStatus; + NTStatus createStatus = share.FileStore.CreateFile(out handle, out fileStatus, path, request.DesiredAccess, request.ShareAccess, request.CreateDisposition, request.CreateOptions); + if (createStatus != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(request.CommandName, createStatus); + } + + ulong? persistentFileID = session.AddOpenFile(path, handle); + if (!persistentFileID.HasValue) + { + share.FileStore.CloseFile(handle); + return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES); + } + if (share is NamedPipeShare) { - Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path); - if (pipeStream != null) - { - ulong? persistentFileID = session.AddOpenFile(path, pipeStream); - if (!persistentFileID.HasValue) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES); - } - return CreateResponseForNamedPipe(persistentFileID.Value, FileStatus.FILE_OPENED); - } - else - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_OBJECT_PATH_NOT_FOUND); - } + return CreateResponseForNamedPipe(persistentFileID.Value, FileStatus.FILE_OPENED); } else { - FileSystemShare fileSystemShare = (FileSystemShare)share; - - FileSystemEntry entry; - Stream stream; - FileStatus fileStatus; - NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, out stream, out fileStatus, fileSystemShare.FileSystem, path, request.DesiredAccess, request.ShareAccess, request.CreateDisposition, request.CreateOptions, state); - if (createStatus != NTStatus.STATUS_SUCCESS) - { - return new ErrorResponse(request.CommandName, createStatus); - } - - bool deleteOnClose = (stream != null) && ((request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0); - ulong? persistentFileID = session.AddOpenFile(path, stream, deleteOnClose); - if (!persistentFileID.HasValue) - { - if (stream != null) - { - stream.Close(); - } - return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES); - } - - CreateResponse response = CreateResponseFromFileSystemEntry(entry, persistentFileID.Value, fileStatus); + FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle); + CreateResponse response = CreateResponseFromFileSystemEntry(fileInfo, persistentFileID.Value, fileStatus); if (request.RequestedOplockLevel == OplockLevel.Batch) { response.OplockLevel = OplockLevel.Batch; @@ -91,24 +72,17 @@ namespace SMBLibrary.Server.SMB2 return response; } - private static CreateResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ulong persistentFileID, FileStatus fileStatus) + private static CreateResponse CreateResponseFromFileSystemEntry(FileNetworkOpenInformation fileInfo, ulong persistentFileID, FileStatus fileStatus) { CreateResponse response = new CreateResponse(); - if (entry.IsDirectory) - { - response.FileAttributes = FileAttributes.Directory; - } - else - { - response.FileAttributes = FileAttributes.Normal; - } response.CreateAction = (CreateAction)fileStatus; - response.CreationTime = entry.CreationTime; - response.LastWriteTime = entry.LastWriteTime; - response.ChangeTime = entry.LastWriteTime; - response.LastAccessTime = entry.LastAccessTime; - response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - response.EndofFile = (long)entry.Size; + response.CreationTime = fileInfo.CreationTime; + response.LastWriteTime = fileInfo.LastWriteTime; + response.ChangeTime = fileInfo.LastWriteTime; + response.LastAccessTime = fileInfo.LastAccessTime; + response.AllocationSize = fileInfo.AllocationSize; + response.EndofFile = fileInfo.EndOfFile; + response.FileAttributes = fileInfo.FileAttributes; response.FileId.Persistent = persistentFileID; return response; } diff --git a/SMBLibrary/Server/SMB2/IOCtlHelper.cs b/SMBLibrary/Server/SMB2/IOCtlHelper.cs index 044ed69..d0f448e 100644 --- a/SMBLibrary/Server/SMB2/IOCtlHelper.cs +++ b/SMBLibrary/Server/SMB2/IOCtlHelper.cs @@ -39,8 +39,18 @@ namespace SMBLibrary.Server.SMB2 { IOCtlResponse response = new IOCtlResponse(); response.CtlCode = request.CtlCode; - openFile.Stream.Write(request.Input, 0, request.Input.Length); - response.Output = ByteReader.ReadAllBytes(openFile.Stream); + int numberOfBytesWritten; + NTStatus writeStatus = share.FileStore.WriteFile(out numberOfBytesWritten, openFile.Handle, 0, request.Input); + if (writeStatus != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(request.CommandName, writeStatus); + } + int maxCount = (int)request.MaxOutputResponse; + NTStatus readStatus = share.FileStore.ReadFile(out response.Output, openFile.Handle, 0, maxCount); + if (readStatus != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(request.CommandName, readStatus); + } return response; } } diff --git a/SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs b/SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs index ff4e396..a7a814d 100644 --- a/SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs +++ b/SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs @@ -34,19 +34,6 @@ namespace SMBLibrary.Server.SMB2 } FileSystemShare fileSystemShare = (FileSystemShare)share; - IFileSystem fileSystem = fileSystemShare.FileSystem; - - if (!fileSystem.GetEntry(openFile.Path).IsDirectory) - { - if ((request.Flags & QueryDirectoryFlags.SMB2_REOPEN) > 0) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED); - } - else - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER); - } - } ulong fileID = request.FileId.Persistent; OpenSearch openSearch = session.GetOpenSearch(fileID); @@ -56,8 +43,8 @@ namespace SMBLibrary.Server.SMB2 { session.RemoveOpenSearch(fileID); } - List entries; - NTStatus searchStatus = NTFileSystemHelper.FindEntries(out entries, fileSystemShare.FileSystem, openFile.Path, request.FileName); + List entries; + NTStatus searchStatus = share.FileStore.QueryDirectory(out entries, openFile.Handle, request.FileName, request.FileInformationClass); if (searchStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "Query Directory: Path: '{0}', Searched for '{1}', NTStatus: {2}", openFile.Path, request.FileName, searchStatus.ToString()); @@ -88,14 +75,11 @@ namespace SMBLibrary.Server.SMB2 int pageLength = 0; for (int index = openSearch.EnumerationLocation; index < openSearch.Entries.Count; index++) { - QueryDirectoryFileInformation fileInformation; - try + QueryDirectoryFileInformation fileInformation = openSearch.Entries[index]; + if (fileInformation.FileInformationClass != request.FileInformationClass) { - fileInformation = FromFileSystemEntry(openSearch.Entries[index], request.FileInformationClass); - } - catch (UnsupportedInformationLevelException) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS); + // We do not support changing FileInformationClass during a search (unless SMB2_REOPEN is set). + return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER); } if (pageLength + fileInformation.Length <= request.OutputBufferLength) @@ -119,95 +103,5 @@ namespace SMBLibrary.Server.SMB2 response.SetFileInformationList(page); return response; } - - internal static QueryDirectoryFileInformation FromFileSystemEntry(FileSystemEntry entry, FileInformationClass informationClass) - { - switch (informationClass) - { - case FileInformationClass.FileBothDirectoryInformation: - { - FileBothDirectoryInformation result = new FileBothDirectoryInformation(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.ChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry); - result.EaSize = 0; - result.ShortName = NTFileSystemHelper.GetShortName(entry.Name); - result.FileName = entry.Name; - return result; - } - case FileInformationClass.FileDirectoryInformation: - { - FileDirectoryInformation result = new FileDirectoryInformation(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.ChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry); - result.FileName = entry.Name; - return result; - } - case FileInformationClass.FileFullDirectoryInformation: - { - FileFullDirectoryInformation result = new FileFullDirectoryInformation(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.ChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry); - result.EaSize = 0; - result.FileName = entry.Name; - return result; - } - case FileInformationClass.FileIdBothDirectoryInformation: - { - FileIdBothDirectoryInformation result = new FileIdBothDirectoryInformation(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.ChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry); - result.EaSize = 0; - result.ShortName = NTFileSystemHelper.GetShortName(entry.Name); - result.FileId = 0; - result.FileName = entry.Name; - return result; - } - case FileInformationClass.FileIdFullDirectoryInformation: - { - FileIdFullDirectoryInformation result = new FileIdFullDirectoryInformation(); - result.CreationTime = entry.CreationTime; - result.LastAccessTime = entry.LastAccessTime; - result.LastWriteTime = entry.LastWriteTime; - result.ChangeTime = entry.LastWriteTime; - result.EndOfFile = (long)entry.Size; - result.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); - result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry); - result.EaSize = 0; - result.FileId = 0; - result.FileName = entry.Name; - return result; - } - case FileInformationClass.FileNamesInformation: - { - FileNamesInformation result = new FileNamesInformation(); - result.FileName = entry.Name; - return result; - } - default: - { - throw new UnsupportedInformationLevelException(); - } - } - } } } diff --git a/SMBLibrary/Server/SMB2/QueryInfoHelper.cs b/SMBLibrary/Server/SMB2/QueryInfoHelper.cs index 0a3c407..c6927cb 100644 --- a/SMBLibrary/Server/SMB2/QueryInfoHelper.cs +++ b/SMBLibrary/Server/SMB2/QueryInfoHelper.cs @@ -25,27 +25,16 @@ namespace SMBLibrary.Server.SMB2 return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED); } - FileInformation fileInformation; - NTStatus queryStatus; - if (share is NamedPipeShare) - { - queryStatus = NTFileSystemHelper.GetNamedPipeInformation(out fileInformation, request.FileInformationClass); - } - else // FileSystemShare + if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.UserName, openFile.Path, state.ClientEndPoint)) { return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED); } - IFileSystem fileSystem = ((FileSystemShare)share).FileSystem; - FileSystemEntry entry = fileSystem.GetEntry(openFile.Path); - if (entry == null) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_SUCH_FILE); - } - queryStatus = NTFileSystemHelper.GetFileInformation(out fileInformation, entry, openFile.DeleteOnClose, request.FileInformationClass); } + FileInformation fileInformation; + NTStatus queryStatus = share.FileStore.GetFileInformation(out fileInformation, openFile.Handle, request.FileInformationClass); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, request.FileInformationClass, queryStatus); @@ -64,9 +53,9 @@ namespace SMBLibrary.Server.SMB2 { return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED); } - IFileSystem fileSystem = ((FileSystemShare)share).FileSystem; + FileSystemInformation fileSystemInformation; - NTStatus queryStatus = NTFileSystemHelper.GetFileSystemInformation(out fileSystemInformation, request.FileSystemInformationClass, fileSystem); + NTStatus queryStatus = share.FileStore.GetFileSystemInformation(out fileSystemInformation, request.FileSystemInformationClass); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information class: {0}, NTStatus: {1}", request.FileSystemInformationClass, queryStatus); diff --git a/SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs b/SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs index 3a8bec0..6906527 100644 --- a/SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs +++ b/SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs @@ -24,7 +24,7 @@ namespace SMBLibrary.Server.SMB2 } byte[] data; - NTStatus readStatus = NTFileSystemHelper.ReadFile(out data, openFile, (long)request.Offset, (int)request.ReadLength, state); + NTStatus readStatus = share.FileStore.ReadFile(out data, openFile.Handle, (long)request.Offset, (int)request.ReadLength); if (readStatus != NTStatus.STATUS_SUCCESS) { return new ErrorResponse(request.CommandName, readStatus); @@ -44,7 +44,7 @@ namespace SMBLibrary.Server.SMB2 } int numberOfBytesWritten; - NTStatus writeStatus = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, (long)request.Offset, request.Data, state); + NTStatus writeStatus = share.FileStore.WriteFile(out numberOfBytesWritten, openFile.Handle, (long)request.Offset, request.Data); if (writeStatus != NTStatus.STATUS_SUCCESS) { return new ErrorResponse(request.CommandName, writeStatus); diff --git a/SMBLibrary/Server/SMB2/SetInfoHelper.cs b/SMBLibrary/Server/SMB2/SetInfoHelper.cs index 0cfd56f..c1a1ba7 100644 --- a/SMBLibrary/Server/SMB2/SetInfoHelper.cs +++ b/SMBLibrary/Server/SMB2/SetInfoHelper.cs @@ -27,47 +27,37 @@ namespace SMBLibrary.Server.SMB2 if (share is FileSystemShare) { - IFileSystem fileSystem = ((FileSystemShare)share).FileSystem; if (!((FileSystemShare)share).HasWriteAccess(session.UserName, openFile.Path, state.ClientEndPoint)) { return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED); } - - FileInformation information; - try - { - information = FileInformation.GetFileInformation(request.Buffer, 0, request.FileInformationClass); - } - catch (UnsupportedInformationLevelException) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS); - } - catch (NotImplementedException) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED); - } - catch (Exception) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER); - } - - if (information is FileRenameInformationType2) - { - string newFileName = ((FileRenameInformationType2)information).FileName; - if (!((FileSystemShare)share).HasWriteAccess(session.UserName, newFileName, state.ClientEndPoint)) - { - return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED); - } - } - - NTStatus status = NTFileSystemHelper.SetFileInformation(fileSystem, openFile, information, state); - if (status != NTStatus.STATUS_SUCCESS) - { - state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, information.FileInformationClass, status); - return new ErrorResponse(request.CommandName, status); - } - return new SetInfoResponse(); } + + FileInformation information; + try + { + information = FileInformation.GetFileInformation(request.Buffer, 0, request.FileInformationClass); + } + catch (UnsupportedInformationLevelException) + { + return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS); + } + catch (NotImplementedException) + { + return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED); + } + catch (Exception) + { + return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER); + } + + NTStatus status = share.FileStore.SetFileInformation(openFile.Handle, information); + if (status != NTStatus.STATUS_SUCCESS) + { + state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, information.FileInformationClass, status); + return new ErrorResponse(request.CommandName, status); + } + return new SetInfoResponse(); } return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED); } diff --git a/SMBLibrary/Server/SMBServer.SMB1.cs b/SMBLibrary/Server/SMBServer.SMB1.cs index 40ce9ec..e024a6b 100644 --- a/SMBLibrary/Server/SMBServer.SMB1.cs +++ b/SMBLibrary/Server/SMBServer.SMB1.cs @@ -199,7 +199,7 @@ namespace SMBLibrary.Server return new ErrorResponse(command.CommandName); } QueryInformationRequest request = (QueryInformationRequest)command; - return FileSystemResponseHelper.GetQueryInformationResponse(header, request, (FileSystemShare)share); + return FileSystemResponseHelper.GetQueryInformationResponse(header, request, (FileSystemShare)share, state); } else if (command is SetInformationRequest) { @@ -229,7 +229,7 @@ namespace SMBLibrary.Server return new ErrorResponse(command.CommandName); } CheckDirectoryRequest request = (CheckDirectoryRequest)command; - return FileSystemResponseHelper.GetCheckDirectoryResponse(header, request, (FileSystemShare)share); + return FileSystemResponseHelper.GetCheckDirectoryResponse(header, request, (FileSystemShare)share, state); } else if (command is WriteRawRequest) { diff --git a/SMBLibrary/Server/SMBServer.SMB2.cs b/SMBLibrary/Server/SMBServer.SMB2.cs index 9a10b24..2cc76eb 100644 --- a/SMBLibrary/Server/SMBServer.SMB2.cs +++ b/SMBLibrary/Server/SMBServer.SMB2.cs @@ -177,7 +177,11 @@ namespace SMBLibrary.Server { return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED); } - openFile.Stream.Flush(); + NTStatus status = share.FileStore.FlushFileBuffers(openFile.Handle); + if (status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(request.CommandName, status); + } return new FlushResponse(); } else if (command is CloseRequest) diff --git a/SMBLibrary/Server/Shares/FileSystemShare.cs b/SMBLibrary/Server/Shares/FileSystemShare.cs index aeed6de..1791b38 100644 --- a/SMBLibrary/Server/Shares/FileSystemShare.cs +++ b/SMBLibrary/Server/Shares/FileSystemShare.cs @@ -33,14 +33,20 @@ namespace SMBLibrary.Server public class FileSystemShare : ISMBShare { private string m_name; - public IFileSystem m_fileSystem; + private INTFileStore m_fileSystem; public event EventHandler OnAccessRequest; + public FileSystemShare(string shareName, INTFileStore fileSystem) + { + m_name = shareName; + m_fileSystem = fileSystem; + } + public FileSystemShare(string shareName, IFileSystem fileSystem) { m_name = shareName; - m_fileSystem = fileSystem; + m_fileSystem = new NTFileSystemAdapter(fileSystem); } public bool HasReadAccess(string userName, string path, IPEndPoint clientEndPoint) @@ -74,7 +80,7 @@ namespace SMBLibrary.Server } } - public IFileSystem FileSystem + public INTFileStore FileStore { get { diff --git a/SMBLibrary/Server/Shares/ISMBShare.cs b/SMBLibrary/Server/Shares/ISMBShare.cs index 889a4a3..da0c66c 100644 --- a/SMBLibrary/Server/Shares/ISMBShare.cs +++ b/SMBLibrary/Server/Shares/ISMBShare.cs @@ -14,5 +14,10 @@ namespace SMBLibrary.Server { get; } + + INTFileStore FileStore + { + get; + } } } diff --git a/SMBLibrary/Server/Shares/NamedPipeShare.cs b/SMBLibrary/Server/Shares/NamedPipeShare.cs index b09ea79..fe3d2b1 100644 --- a/SMBLibrary/Server/Shares/NamedPipeShare.cs +++ b/SMBLibrary/Server/Shares/NamedPipeShare.cs @@ -12,45 +12,19 @@ using SMBLibrary.Services; namespace SMBLibrary.Server { - public class NamedPipeShare : List, ISMBShare + public class NamedPipeShare : ISMBShare { // A pipe share, as defined by the SMB Protocol, MUST always have the name "IPC$". public const string NamedPipeShareName = "IPC$"; + private NamedPipeStore m_store; + public NamedPipeShare(List shareList) { - this.Add(new ServerService(Environment.MachineName, shareList)); - this.Add(new WorkstationService(Environment.MachineName, Environment.MachineName)); - } - - public Stream OpenPipe(string path) - { - // It is possible to have a named pipe that does not use RPC (e.g. MS-WSP), - // However this is not currently needed by our implementation. - RemoteService service = GetService(path); - if (service != null) - { - // All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles, - // and provides a separate conduit for client/server communication. - return new RPCPipeStream(service); - } - return null; - } - - private RemoteService GetService(string path) - { - if (path.StartsWith(@"\")) - { - path = path.Substring(1); - } - foreach (RemoteService service in this) - { - if (String.Equals(path, service.PipeName, StringComparison.InvariantCultureIgnoreCase)) - { - return service; - } - } - return null; + List services = new List(); + services.Add(new ServerService(Environment.MachineName, shareList)); + services.Add(new WorkstationService(Environment.MachineName, Environment.MachineName)); + m_store = new NamedPipeStore(services); } public string Name @@ -60,5 +34,13 @@ namespace SMBLibrary.Server return NamedPipeShareName; } } + + public INTFileStore FileStore + { + get + { + return m_store; + } + } } }