diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj
index 87a50f8..dd12fde 100644
--- a/SMBLibrary/SMBLibrary.csproj
+++ b/SMBLibrary/SMBLibrary.csproj
@@ -114,6 +114,7 @@
+
diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs b/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs
new file mode 100644
index 0000000..ff61bc7
--- /dev/null
+++ b/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs
@@ -0,0 +1,417 @@
+/* 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 SMBLibrary.Services;
+using Utilities;
+
+namespace SMBLibrary.Server
+{
+ ///
+ /// Helper class to access the FileSystemShare / NamedPipeShare in an NT-like manner dictated by the SMB protocol
+ ///
+ public partial class NTFileSystemHelper
+ {
+ public const int BytesPerSector = 512;
+ public const int ClusterSize = 4096;
+
+ public static NTStatus CreateFile(out FileSystemEntry entry, FileSystemShare share, string userName, string path, CreateDisposition createDisposition, CreateOptions createOptions, AccessMask desiredAccess, ConnectionState state)
+ {
+ bool hasWriteAccess = share.HasWriteAccess(userName);
+ IFileSystem fileSystem = share.FileSystem;
+
+ bool forceDirectory = (createOptions & CreateOptions.FILE_DIRECTORY_FILE) > 0;
+ bool forceFile = (createOptions & CreateOptions.FILE_NON_DIRECTORY_FILE) > 0;
+
+ if (forceDirectory & (createDisposition != CreateDisposition.FILE_CREATE &&
+ createDisposition != CreateDisposition.FILE_OPEN &&
+ createDisposition != CreateDisposition.FILE_OPEN_IF))
+ {
+ entry = null;
+ return NTStatus.STATUS_INVALID_PARAMETER;
+ }
+
+ // Windows will try to access named streams (alternate data streams) regardless of the FILE_NAMED_STREAMS flag, we need to prevent this behaviour.
+ if (path.Contains(":"))
+ {
+ // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND
+ entry = null;
+ return NTStatus.STATUS_NO_SUCH_FILE;
+ }
+
+ entry = fileSystem.GetEntry(path);
+ if (createDisposition == CreateDisposition.FILE_OPEN)
+ {
+ if (entry == null)
+ {
+ return NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ if (entry.IsDirectory && forceFile)
+ {
+ return NTStatus.STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!entry.IsDirectory && forceDirectory)
+ {
+ // Not sure if that's the correct response
+ return NTStatus.STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+ else if (createDisposition == CreateDisposition.FILE_CREATE)
+ {
+ if (entry != null)
+ {
+ // File already exists, fail the request
+ state.LogToServer(Severity.Debug, "NTCreate: File '{0}' already exist", path);
+ return NTStatus.STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (!hasWriteAccess)
+ {
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+
+ try
+ {
+ if (forceDirectory)
+ {
+ state.LogToServer(Severity.Information, "NTCreate: Creating directory '{0}'", path);
+ entry = fileSystem.CreateDirectory(path);
+ }
+ else
+ {
+ state.LogToServer(Severity.Information, "NTCreate: Creating file '{0}'", path);
+ entry = fileSystem.CreateFile(path);
+ }
+ }
+ catch (IOException ex)
+ {
+ ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+ if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+ {
+ state.LogToServer(Severity.Debug, "NTCreate: Sharing violation creating '{0}'", path);
+ return NTStatus.STATUS_SHARING_VIOLATION;
+ }
+ else
+ {
+ state.LogToServer(Severity.Debug, "NTCreate: Error creating '{0}'", path);
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ }
+ catch (UnauthorizedAccessException)
+ {
+ state.LogToServer(Severity.Debug, "NTCreate: Error creating '{0}', Access Denied", path);
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+ }
+ else if (createDisposition == CreateDisposition.FILE_OPEN_IF ||
+ createDisposition == CreateDisposition.FILE_OVERWRITE ||
+ createDisposition == CreateDisposition.FILE_OVERWRITE_IF ||
+ createDisposition == CreateDisposition.FILE_SUPERSEDE)
+ {
+ entry = fileSystem.GetEntry(path);
+ if (entry == null)
+ {
+ if (createDisposition == CreateDisposition.FILE_OVERWRITE)
+ {
+ return NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ if (!hasWriteAccess)
+ {
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+
+ try
+ {
+ if (forceDirectory)
+ {
+ state.LogToServer(Severity.Information, "NTCreate: Creating directory '{0}'", path);
+ entry = fileSystem.CreateDirectory(path);
+ }
+ else
+ {
+ state.LogToServer(Severity.Information, "NTCreate: Creating file '{0}'", path);
+ entry = fileSystem.CreateFile(path);
+ }
+ }
+ catch (IOException ex)
+ {
+ ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+ if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+ {
+ return NTStatus.STATUS_SHARING_VIOLATION;
+ }
+ else
+ {
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ }
+ catch (UnauthorizedAccessException)
+ {
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ if (createDisposition == CreateDisposition.FILE_OVERWRITE ||
+ createDisposition == CreateDisposition.FILE_OVERWRITE_IF ||
+ createDisposition == CreateDisposition.FILE_SUPERSEDE)
+ {
+ if (!hasWriteAccess)
+ {
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+
+ // Truncate the file
+ try
+ {
+ Stream temp = fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite);
+ temp.Close();
+ }
+ catch (IOException ex)
+ {
+ ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+ if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+ {
+ return NTStatus.STATUS_SHARING_VIOLATION;
+ }
+ else
+ {
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ }
+ catch (UnauthorizedAccessException)
+ {
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+ }
+ else
+ {
+ return NTStatus.STATUS_INVALID_PARAMETER;
+ }
+
+ FileAccess fileAccess = ToFileAccess(desiredAccess.File);
+ if (!hasWriteAccess && (fileAccess == FileAccess.Write || fileAccess == FileAccess.ReadWrite))
+ {
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+
+ return NTStatus.STATUS_SUCCESS;
+ }
+
+ public static FileAccess ToFileAccess(FileAccessMask desiredAccess)
+ {
+ if ((desiredAccess & FileAccessMask.GENERIC_ALL) > 0 ||
+ ((desiredAccess & FileAccessMask.FILE_READ_DATA) > 0 && (desiredAccess & FileAccessMask.FILE_WRITE_DATA) > 0) ||
+ ((desiredAccess & FileAccessMask.FILE_READ_DATA) > 0 && (desiredAccess & FileAccessMask.FILE_APPEND_DATA) > 0))
+ {
+ return FileAccess.ReadWrite;
+ }
+ else if ((desiredAccess & FileAccessMask.GENERIC_WRITE) > 0 ||
+ (desiredAccess & FileAccessMask.FILE_WRITE_DATA) > 0 ||
+ (desiredAccess & FileAccessMask.FILE_APPEND_DATA) > 0)
+ {
+ return FileAccess.Write;
+ }
+ else if ((desiredAccess & FileAccessMask.FILE_READ_DATA) > 0)
+ {
+ return FileAccess.Read;
+ }
+ else
+ {
+ return (FileAccess)0;
+ }
+ }
+
+ public static FileShare ToFileShare(ShareAccess shareAccess)
+ {
+ if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0 && (shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0)
+ {
+ return FileShare.ReadWrite;
+ }
+ else if ((shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0)
+ {
+ return FileShare.Write;
+ }
+ else if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0)
+ {
+ return FileShare.Read;
+ }
+ else if ((shareAccess & ShareAccess.FILE_SHARE_DELETE) > 0)
+ {
+ return FileShare.Delete;
+ }
+ else
+ {
+ return FileShare.None;
+ }
+ }
+
+ public static NTStatus ReadFile(out byte[] data, OpenFileObject openFile, long offset, int maxCount, ConnectionState state)
+ {
+ data = null;
+ string openFilePath = openFile.Path;
+ Stream stream = openFile.Stream;
+ if (stream is RPCPipeStream)
+ {
+ 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;
+ }
+ else // File
+ {
+ if (stream == null)
+ {
+ state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}', Invalid Operation.", openFilePath);
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+
+ int bytesRead;
+ try
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+ data = new byte[maxCount];
+ bytesRead = stream.Read(data, 0, maxCount);
+ }
+ 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, "ReadFile: Cannot read '{0}'. Sharing Violation.", openFilePath);
+ return NTStatus.STATUS_SHARING_VIOLATION;
+ }
+ else
+ {
+ state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}'. Data Error.", openFilePath);
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}'. Offset Out Of Range.", openFilePath);
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ catch (UnauthorizedAccessException)
+ {
+ state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}', Access Denied.", openFilePath);
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+
+ if (bytesRead < maxCount)
+ {
+ // EOF, we must trim the response data array
+ data = ByteReader.ReadBytes(data, 0, bytesRead);
+ }
+ return NTStatus.STATUS_SUCCESS;
+ }
+ }
+
+ public static NTStatus WriteFile(out int numberOfBytesWritten, OpenFileObject openFile, long offset, byte[] data, ConnectionState state)
+ {
+ 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)
+ {
+ 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);
+ numberOfBytesWritten = data.Length;
+ return NTStatus.STATUS_SUCCESS;
+ }
+ catch (IOException ex)
+ {
+ ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+ if (errorCode == (ushort)Win32Error.ERROR_DISK_FULL)
+ {
+ state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Disk Full.", openFilePath);
+ return NTStatus.STATUS_DISK_FULL;
+ }
+ else if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+ {
+ state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Sharing Violation.", openFilePath);
+ // Returning STATUS_SHARING_VIOLATION is undocumented but apparently valid
+ return NTStatus.STATUS_SHARING_VIOLATION;
+ }
+ else
+ {
+ state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Data Error.", openFilePath);
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Offset Out Of Range.", openFilePath);
+ return NTStatus.STATUS_DATA_ERROR;
+ }
+ catch (UnauthorizedAccessException)
+ {
+ state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Access Denied.", openFilePath);
+ // The user may have tried to write to a readonly file
+ return NTStatus.STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ ///
+ /// Will return a virtual allocation size, assuming 4096 bytes per cluster
+ ///
+ public static ulong GetAllocationSize(ulong size)
+ {
+ return (ulong)Math.Ceiling((double)size / ClusterSize) * ClusterSize;
+ }
+
+ public static string GetShortName(string fileName)
+ {
+ string fileNameWithoutExt = System.IO.Path.GetFileNameWithoutExtension(fileName);
+ string extension = System.IO.Path.GetExtension(fileName);
+ if (fileNameWithoutExt.Length > 8 || extension.Length > 4)
+ {
+ if (fileNameWithoutExt.Length > 8)
+ {
+ fileNameWithoutExt = fileNameWithoutExt.Substring(0, 8);
+ }
+
+ if (extension.Length > 4)
+ {
+ extension = extension.Substring(0, 4);
+ }
+
+ return fileNameWithoutExt + extension;
+ }
+ else
+ {
+ return fileName;
+ }
+ }
+ }
+}
diff --git a/SMBLibrary/Server/SMB1/InfoHelper.cs b/SMBLibrary/Server/SMB1/InfoHelper.cs
index aa91b5b..bdafb33 100644
--- a/SMBLibrary/Server/SMB1/InfoHelper.cs
+++ b/SMBLibrary/Server/SMB1/InfoHelper.cs
@@ -14,9 +14,6 @@ namespace SMBLibrary.Server.SMB1
{
public class InfoHelper
{
- public const int BytesPerSector = 512;
- public const int ClusterSize = 4096;
-
internal static FindInformation FromFileSystemEntry(FileSystemEntry entry, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys)
{
switch (informationLevel)
@@ -28,7 +25,7 @@ namespace SMBLibrary.Server.SMB1
result.LastAccessDateTime = entry.LastAccessTime;
result.LastWriteDateTime = entry.LastWriteTime;
result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue);
- result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue);
+ result.AllocationSize = (uint)Math.Min(NTFileSystemHelper.GetAllocationSize(entry.Size), UInt32.MaxValue);
result.Attributes = GetFileAttributes(entry);
result.FileName = entry.Name;
return result;
@@ -40,7 +37,7 @@ namespace SMBLibrary.Server.SMB1
result.LastAccessDateTime = entry.LastAccessTime;
result.LastWriteDateTime = entry.LastWriteTime;
result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue);
- result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue);
+ result.AllocationSize = (uint)Math.Min(NTFileSystemHelper.GetAllocationSize(entry.Size), UInt32.MaxValue);
result.Attributes = GetFileAttributes(entry);
result.EASize = 0;
result.FileName = entry.Name;
@@ -53,7 +50,7 @@ namespace SMBLibrary.Server.SMB1
result.LastAccessDateTime = entry.LastAccessTime;
result.LastWriteDateTime = entry.LastWriteTime;
result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue);
- result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue);
+ result.AllocationSize = (uint)Math.Min(NTFileSystemHelper.GetAllocationSize(entry.Size), UInt32.MaxValue);
result.Attributes = GetFileAttributes(entry);
result.ExtendedAttributeList = new FullExtendedAttributeList();
return result;
@@ -66,7 +63,7 @@ namespace SMBLibrary.Server.SMB1
result.LastWriteTime = entry.LastWriteTime;
result.LastAttrChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
- result.AllocationSize = GetAllocationSize(entry.Size);
+ result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.ExtFileAttributes = GetExtendedFileAttributes(entry);
result.FileName = entry.Name;
return result;
@@ -79,7 +76,7 @@ namespace SMBLibrary.Server.SMB1
result.LastWriteTime = entry.LastWriteTime;
result.LastAttrChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
- result.AllocationSize = GetAllocationSize(entry.Size);
+ result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.ExtFileAttributes = GetExtendedFileAttributes(entry);
result.FileName = entry.Name;
return result;
@@ -98,9 +95,9 @@ namespace SMBLibrary.Server.SMB1
result.LastWriteTime = entry.LastWriteTime;
result.LastChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
- result.AllocationSize = GetAllocationSize(entry.Size);
+ result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.ExtFileAttributes = GetExtendedFileAttributes(entry);
- result.ShortName = GetShortName(entry.Name);
+ result.ShortName = NTFileSystemHelper.GetShortName(entry.Name);
result.FileName = entry.Name;
return result;
}
@@ -122,7 +119,7 @@ namespace SMBLibrary.Server.SMB1
result.LastAccessDateTime = entry.LastAccessTime;
result.LastWriteDateTime = entry.LastWriteTime;
result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue);
- result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue);
+ result.AllocationSize = (uint)Math.Min(NTFileSystemHelper.GetAllocationSize(entry.Size), UInt32.MaxValue);
return result;
}
case QueryInformationLevel.SMB_INFO_QUERY_EA_SIZE:
@@ -132,7 +129,7 @@ namespace SMBLibrary.Server.SMB1
result.LastAccessDateTime = entry.LastAccessTime;
result.LastWriteDateTime = entry.LastWriteTime;
result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue);
- result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue);
+ result.AllocationSize = (uint)Math.Min(NTFileSystemHelper.GetAllocationSize(entry.Size), UInt32.MaxValue);
result.Attributes = GetFileAttributes(entry);
result.EASize = 0;
return result;
@@ -162,7 +159,7 @@ namespace SMBLibrary.Server.SMB1
case QueryInformationLevel.SMB_QUERY_FILE_STANDARD_INFO:
{
QueryFileStandardInfo result = new QueryFileStandardInfo();
- result.AllocationSize = GetAllocationSize(entry.Size);
+ result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.EndOfFile = entry.Size;
result.DeletePending = deletePending;
result.Directory = entry.IsDirectory;
@@ -188,7 +185,7 @@ namespace SMBLibrary.Server.SMB1
result.LastWriteDateTime = entry.LastWriteTime;
result.ExtFileAttributes = GetExtendedFileAttributes(entry);
result.LastChangeTime = entry.LastWriteTime;
- result.AllocationSize = GetAllocationSize(entry.Size);
+ result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.EndOfFile = entry.Size;
result.DeletePending = deletePending;
result.Directory = entry.IsDirectory;
@@ -199,14 +196,14 @@ namespace SMBLibrary.Server.SMB1
case QueryInformationLevel.SMB_QUERY_FILE_ALT_NAME_INFO:
{
QueryFileAltNameInfo result = new QueryFileAltNameInfo();
- result.FileName = GetShortName(entry.Name);
+ result.FileName = NTFileSystemHelper.GetShortName(entry.Name);
return result;
}
case QueryInformationLevel.SMB_QUERY_FILE_STREAM_INFO:
{
QueryFileStreamInfo result = new QueryFileStreamInfo();
result.StreamSize = entry.Size;
- result.StreamAllocationSize = GetAllocationSize(entry.Size);
+ result.StreamAllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.StreamName = "::$DATA";
return result;
}
@@ -231,10 +228,10 @@ namespace SMBLibrary.Server.SMB1
{
QueryFSInfoAllocation result = new QueryFSInfoAllocation();
result.FileSystemID = 0;
- result.SectorUnit = ClusterSize / BytesPerSector;
- result.UnitsTotal = (uint)Math.Min(fileSystem.Size / ClusterSize, UInt32.MaxValue);
- result.UnitsAvailable = (uint)Math.Min(fileSystem.FreeSpace / ClusterSize, UInt32.MaxValue);
- result.Sector = BytesPerSector;
+ result.SectorUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector;
+ result.UnitsTotal = (uint)Math.Min(fileSystem.Size / NTFileSystemHelper.ClusterSize, UInt32.MaxValue);
+ result.UnitsAvailable = (uint)Math.Min(fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize, UInt32.MaxValue);
+ result.Sector = NTFileSystemHelper.BytesPerSector;
return result;
}
case QueryFSInformationLevel.SMB_INFO_VOLUME:
@@ -253,10 +250,10 @@ namespace SMBLibrary.Server.SMB1
case QueryFSInformationLevel.SMB_QUERY_FS_SIZE_INFO:
{
QueryFSSizeInfo result = new QueryFSSizeInfo();
- result.TotalAllocationUnits = (ulong)(fileSystem.Size / ClusterSize);
- result.TotalFreeAllocationUnits = (ulong)(fileSystem.FreeSpace / ClusterSize);
- result.BytesPerSector = BytesPerSector;
- result.SectorsPerAllocationUnit = ClusterSize / BytesPerSector;
+ result.TotalAllocationUnits = (ulong)(fileSystem.Size / NTFileSystemHelper.ClusterSize);
+ result.TotalFreeAllocationUnits = (ulong)(fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize);
+ result.BytesPerSector = NTFileSystemHelper.BytesPerSector;
+ result.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector;
return result;
}
case QueryFSInformationLevel.SMB_QUERY_FS_DEVICE_INFO:
@@ -281,39 +278,6 @@ namespace SMBLibrary.Server.SMB1
}
}
- ///
- /// Will return a virtual allocation size, assuming 4096 bytes per cluster
- ///
- public static ulong GetAllocationSize(ulong size)
- {
- return (ulong)Math.Ceiling((double)size / ClusterSize) * ClusterSize;
- }
-
- private static string GetShortName(string filename)
- {
- string fileNameWithoutExt = System.IO.Path.GetFileNameWithoutExtension(filename);
- string extension = System.IO.Path.GetExtension(filename);
- if (fileNameWithoutExt.Length > 8 || extension.Length > 4)
- {
- if (fileNameWithoutExt.Length > 8)
- {
- fileNameWithoutExt = fileNameWithoutExt.Substring(0, 8);
- }
-
- if (extension.Length > 4)
- {
- extension = extension.Substring(0, 4);
- }
-
- return fileNameWithoutExt + extension;
- }
- else
- {
- return filename;
- }
- }
-
-
public static SMBFileAttributes GetFileAttributes(FileSystemEntry entry)
{
SMBFileAttributes attributes = SMBFileAttributes.Normal;
@@ -339,7 +303,7 @@ namespace SMBLibrary.Server.SMB1
public static ExtendedFileAttributes GetExtendedFileAttributes(FileSystemEntry entry)
{
- ExtendedFileAttributes attributes = (ExtendedFileAttributes)0;
+ ExtendedFileAttributes attributes = 0;
if (entry.IsHidden)
{
attributes |= ExtendedFileAttributes.Hidden;
@@ -357,7 +321,7 @@ namespace SMBLibrary.Server.SMB1
attributes |= ExtendedFileAttributes.Directory;
}
- if (attributes == (ExtendedFileAttributes)0)
+ if ((uint)attributes == 0)
{
attributes = ExtendedFileAttributes.Normal;
}
diff --git a/SMBLibrary/Server/SMB1/NTCreateHelper.cs b/SMBLibrary/Server/SMB1/NTCreateHelper.cs
index 83fd6f8..5036546 100644
--- a/SMBLibrary/Server/SMB1/NTCreateHelper.cs
+++ b/SMBLibrary/Server/SMB1/NTCreateHelper.cs
@@ -50,7 +50,7 @@ namespace SMBLibrary.Server.SMB1
FileSystemShare fileSystemShare = (FileSystemShare)share;
string userName = session.UserName;
FileSystemEntry entry;
- NTStatus createStatus = CreateFile(out entry, fileSystemShare, userName, path, request.CreateDisposition, request.CreateOptions, request.DesiredAccess, state);
+ NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, fileSystemShare, userName, path, request.CreateDisposition, request.CreateOptions, request.DesiredAccess, state);
if (createStatus != NTStatus.STATUS_SUCCESS)
{
header.Status = createStatus;
@@ -58,8 +58,8 @@ namespace SMBLibrary.Server.SMB1
}
IFileSystem fileSystem = fileSystemShare.FileSystem;
- FileAccess fileAccess = ToFileAccess(request.DesiredAccess);
- FileShare fileShare = ToFileShare(request.ShareAccess);
+ FileAccess fileAccess = NTFileSystemHelper.ToFileAccess(request.DesiredAccess);
+ FileShare fileShare = NTFileSystemHelper.ToFileShare(request.ShareAccess);
Stream stream;
bool deleteOnClose = false;
@@ -137,242 +137,6 @@ namespace SMBLibrary.Server.SMB1
}
}
- public static NTStatus CreateFile(out FileSystemEntry entry, FileSystemShare share, string userName, string path, CreateDisposition createDisposition, CreateOptions createOptions, AccessMask desiredAccess, ConnectionState state)
- {
- bool hasWriteAccess = share.HasWriteAccess(userName);
- IFileSystem fileSystem = share.FileSystem;
-
- bool forceDirectory = (createOptions & CreateOptions.FILE_DIRECTORY_FILE) > 0;
- bool forceFile = (createOptions & CreateOptions.FILE_NON_DIRECTORY_FILE) > 0;
-
- if (forceDirectory & (createDisposition != CreateDisposition.FILE_CREATE &&
- createDisposition != CreateDisposition.FILE_OPEN &&
- createDisposition != CreateDisposition.FILE_OPEN_IF))
- {
- entry = null;
- return NTStatus.STATUS_INVALID_PARAMETER;
- }
-
- // Windows will try to access named streams (alternate data streams) regardless of the FILE_NAMED_STREAMS flag, we need to prevent this behaviour.
- if (path.Contains(":"))
- {
- // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND
- entry = null;
- return NTStatus.STATUS_NO_SUCH_FILE;
- }
-
- entry = fileSystem.GetEntry(path);
- if (createDisposition == CreateDisposition.FILE_OPEN)
- {
- if (entry == null)
- {
- return NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
- }
-
- if (entry.IsDirectory && forceFile)
- {
- return NTStatus.STATUS_FILE_IS_A_DIRECTORY;
- }
-
- if (!entry.IsDirectory && forceDirectory)
- {
- // Not sure if that's the correct response
- return NTStatus.STATUS_OBJECT_NAME_COLLISION;
- }
- }
- else if (createDisposition == CreateDisposition.FILE_CREATE)
- {
- if (entry != null)
- {
- // File already exists, fail the request
- state.LogToServer(Severity.Debug, "NTCreate: File '{0}' already exist", path);
- return NTStatus.STATUS_OBJECT_NAME_COLLISION;
- }
-
- if (!hasWriteAccess)
- {
- return NTStatus.STATUS_ACCESS_DENIED;
- }
-
- try
- {
- if (forceDirectory)
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating directory '{0}'", path);
- entry = fileSystem.CreateDirectory(path);
- }
- else
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating file '{0}'", path);
- entry = fileSystem.CreateFile(path);
- }
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- state.LogToServer(Severity.Debug, "NTCreate: Sharing violation creating '{0}'", path);
- return NTStatus.STATUS_SHARING_VIOLATION;
- }
- else
- {
- state.LogToServer(Severity.Debug, "NTCreate: Error creating '{0}'", path);
- return NTStatus.STATUS_DATA_ERROR;
- }
- }
- catch (UnauthorizedAccessException)
- {
- state.LogToServer(Severity.Debug, "NTCreate: Error creating '{0}', Access Denied", path);
- return NTStatus.STATUS_ACCESS_DENIED;
- }
- }
- else if (createDisposition == CreateDisposition.FILE_OPEN_IF ||
- createDisposition == CreateDisposition.FILE_OVERWRITE ||
- createDisposition == CreateDisposition.FILE_OVERWRITE_IF ||
- createDisposition == CreateDisposition.FILE_SUPERSEDE)
- {
- entry = fileSystem.GetEntry(path);
- if (entry == null)
- {
- if (createDisposition == CreateDisposition.FILE_OVERWRITE)
- {
- return NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
- }
-
- if (!hasWriteAccess)
- {
- return NTStatus.STATUS_ACCESS_DENIED;
- }
-
- try
- {
- if (forceDirectory)
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating directory '{0}'", path);
- entry = fileSystem.CreateDirectory(path);
- }
- else
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating file '{0}'", path);
- entry = fileSystem.CreateFile(path);
- }
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- return NTStatus.STATUS_SHARING_VIOLATION;
- }
- else
- {
- return NTStatus.STATUS_DATA_ERROR;
- }
- }
- catch (UnauthorizedAccessException)
- {
- return NTStatus.STATUS_ACCESS_DENIED;
- }
- }
- else
- {
- if (createDisposition == CreateDisposition.FILE_OVERWRITE ||
- createDisposition == CreateDisposition.FILE_OVERWRITE_IF ||
- createDisposition == CreateDisposition.FILE_SUPERSEDE)
- {
- if (!hasWriteAccess)
- {
- return NTStatus.STATUS_ACCESS_DENIED;
- }
-
- // Truncate the file
- try
- {
- Stream temp = fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite);
- temp.Close();
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- return NTStatus.STATUS_SHARING_VIOLATION;
- }
- else
- {
- return NTStatus.STATUS_DATA_ERROR;
- }
- }
- catch (UnauthorizedAccessException)
- {
- return NTStatus.STATUS_ACCESS_DENIED;
- }
- }
- }
- }
- else
- {
- return NTStatus.STATUS_INVALID_PARAMETER;
- }
-
- FileAccess fileAccess = ToFileAccess(desiredAccess.File);
- if (!hasWriteAccess && (fileAccess == FileAccess.Write || fileAccess == FileAccess.ReadWrite))
- {
- return NTStatus.STATUS_ACCESS_DENIED;
- }
-
- return NTStatus.STATUS_SUCCESS;
- }
-
- public static FileAccess ToFileAccess(FileAccessMask desiredAccess)
- {
- if ((desiredAccess & FileAccessMask.GENERIC_ALL) > 0 ||
- ((desiredAccess & FileAccessMask.FILE_READ_DATA) > 0 && (desiredAccess & FileAccessMask.FILE_WRITE_DATA) > 0) ||
- ((desiredAccess & FileAccessMask.FILE_READ_DATA) > 0 && (desiredAccess & FileAccessMask.FILE_APPEND_DATA) > 0))
- {
- return FileAccess.ReadWrite;
- }
- else if ((desiredAccess & FileAccessMask.GENERIC_WRITE) > 0 ||
- (desiredAccess & FileAccessMask.FILE_WRITE_DATA) > 0 ||
- (desiredAccess & FileAccessMask.FILE_APPEND_DATA) > 0)
- {
- return FileAccess.Write;
- }
- else if ((desiredAccess & FileAccessMask.FILE_READ_DATA) > 0)
- {
- return FileAccess.Read;
- }
- else
- {
- return (FileAccess)0;
- }
- }
-
- public static FileShare ToFileShare(ShareAccess shareAccess)
- {
- if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0 && (shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0)
- {
- return FileShare.ReadWrite;
- }
- else if ((shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0)
- {
- return FileShare.Write;
- }
- else if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0)
- {
- return FileShare.Read;
- }
- else if ((shareAccess & ShareAccess.FILE_SHARE_DELETE) > 0)
- {
- return FileShare.Delete;
- }
- else
- {
- return FileShare.None;
- }
- }
-
private static NTCreateAndXResponse CreateResponseForNamedPipe(ushort fileID)
{
NTCreateAndXResponse response = new NTCreateAndXResponse();
@@ -424,7 +188,7 @@ namespace SMBLibrary.Server.SMB1
}
response.FID = fileID;
response.CreateDisposition = CreateDisposition.FILE_OPEN;
- response.AllocationSize = InfoHelper.GetAllocationSize(entry.Size);
+ response.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
response.EndOfFile = entry.Size;
response.CreateTime = entry.CreationTime;
response.LastAccessTime = entry.LastAccessTime;
@@ -452,7 +216,7 @@ namespace SMBLibrary.Server.SMB1
response.LastWriteTime = entry.LastWriteTime;
response.LastChangeTime = entry.LastWriteTime;
response.CreateDisposition = CreateDisposition.FILE_OPEN;
- response.AllocationSize = InfoHelper.GetAllocationSize(entry.Size);
+ response.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
response.EndOfFile = entry.Size;
response.ResourceType = ResourceType.FileTypeDisk;
response.FileStatus = FileStatus.NO_EAS | FileStatus.NO_SUBSTREAMS | FileStatus.NO_REPARSETAG;
diff --git a/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs b/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs
index 2c8f43b..3b221fc 100644
--- a/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs
+++ b/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs
@@ -27,7 +27,7 @@ namespace SMBLibrary.Server.SMB1
return null;
}
byte[] data;
- header.Status = ReadFile(out data, openFile, request.ReadOffsetInBytes, request.CountOfBytesToRead, state);
+ header.Status = NTFileSystemHelper.ReadFile(out data, openFile, request.ReadOffsetInBytes, request.CountOfBytesToRead, state);
if (header.Status != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName);
@@ -54,7 +54,7 @@ namespace SMBLibrary.Server.SMB1
maxCount = request.MaxCountLarge;
}
byte[] data;
- header.Status = ReadFile(out data, openFile, (long)request.Offset, (int)maxCount, state);
+ header.Status = NTFileSystemHelper.ReadFile(out data, openFile, (long)request.Offset, (int)maxCount, state);
if (header.Status != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName);
@@ -70,72 +70,6 @@ namespace SMBLibrary.Server.SMB1
return response;
}
- public static NTStatus ReadFile(out byte[] data, OpenFileObject openFile, long offset, int maxCount, ConnectionState state)
- {
- data = null;
- string openFilePath = openFile.Path;
- Stream stream = openFile.Stream;
- if (stream is RPCPipeStream)
- {
- 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;
- }
- else // File
- {
- if (stream == null)
- {
- state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}', Invalid Operation.", openFilePath);
- return NTStatus.STATUS_ACCESS_DENIED;
- }
-
- int bytesRead;
- try
- {
- stream.Seek(offset, SeekOrigin.Begin);
- data = new byte[maxCount];
- bytesRead = stream.Read(data, 0, maxCount);
- }
- 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, "ReadFile: Cannot read '{0}'. Sharing Violation.", openFilePath);
- return NTStatus.STATUS_SHARING_VIOLATION;
- }
- else
- {
- state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}'. Data Error.", openFilePath);
- return NTStatus.STATUS_DATA_ERROR;
- }
- }
- catch (ArgumentOutOfRangeException)
- {
- state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}'. Offset Out Of Range.", openFilePath);
- return NTStatus.STATUS_DATA_ERROR;
- }
- catch (UnauthorizedAccessException)
- {
- state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}', Access Denied.", openFilePath);
- return NTStatus.STATUS_ACCESS_DENIED;
- }
-
- if (bytesRead < maxCount)
- {
- // EOF, we must trim the response data array
- data = ByteReader.ReadBytes(data, 0, bytesRead);
- }
- return NTStatus.STATUS_SUCCESS;
- }
- }
-
internal static SMB1Command GetWriteResponse(SMB1Header header, WriteRequest request, ISMBShare share, SMB1ConnectionState state)
{
SMB1Session session = state.GetSession(header.UID);
@@ -146,7 +80,7 @@ namespace SMBLibrary.Server.SMB1
return new ErrorResponse(request.CommandName);
}
int numberOfBytesWritten;
- header.Status = WriteFile(out numberOfBytesWritten, openFile, request.WriteOffsetInBytes, request.Data, state);
+ header.Status = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, request.WriteOffsetInBytes, request.Data, state);
if (header.Status != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName);
@@ -166,7 +100,7 @@ namespace SMBLibrary.Server.SMB1
return new ErrorResponse(request.CommandName);
}
int numberOfBytesWritten;
- header.Status = WriteFile(out numberOfBytesWritten, openFile, (long)request.Offset, request.Data, state);
+ header.Status = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, (long)request.Offset, request.Data, state);
if (header.Status != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName);
@@ -180,65 +114,5 @@ namespace SMBLibrary.Server.SMB1
}
return response;
}
-
- public static NTStatus WriteFile(out int numberOfBytesWritten, OpenFileObject openFile, long offset, byte[] data, ConnectionState state)
- {
- 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)
- {
- 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);
- numberOfBytesWritten = data.Length;
- return NTStatus.STATUS_SUCCESS;
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_DISK_FULL)
- {
- state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Disk Full.", openFilePath);
- return NTStatus.STATUS_DISK_FULL;
- }
- else if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Sharing Violation.", openFilePath);
- // Returning STATUS_SHARING_VIOLATION is undocumented but apparently valid
- return NTStatus.STATUS_SHARING_VIOLATION;
- }
- else
- {
- state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Data Error.", openFilePath);
- return NTStatus.STATUS_DATA_ERROR;
- }
- }
- catch (ArgumentOutOfRangeException)
- {
- state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Offset Out Of Range.", openFilePath);
- return NTStatus.STATUS_DATA_ERROR;
- }
- catch (UnauthorizedAccessException)
- {
- state.LogToServer(Severity.Debug, "WriteFile: Cannot write '{0}'. Access Denied.", openFilePath);
- // The user may have tried to write to a readonly file
- return NTStatus.STATUS_ACCESS_DENIED;
- }
- }
- }
}
}