INTFileStore interface was added for better separation between the object store layer and the SMB layer

This commit is contained in:
Tal Aloni 2017-02-11 11:37:17 +02:00
parent cc170322ed
commit fb43fb966d
43 changed files with 1715 additions and 1487 deletions

View file

@ -0,0 +1,29 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}

View file

@ -9,36 +9,33 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using Utilities; 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: entry = m_fileSystem.GetEntry(path);
{ }
FileBasicInformation information = new FileBasicInformation(); catch (Exception ex)
information.FileAttributes = FileAttributes.Temporary; {
result = information; NTStatus status = ToNTStatus(ex);
return NTStatus.STATUS_SUCCESS; Log(Severity.Debug, "GetFileInformation on '{0}' failed. {1}", path, status);
} result = null;
case FileInformationClass.FileStandardInformation: return status;
{ }
FileStandardInformation information = new FileStandardInformation();
information.DeletePending = true; if (entry == null)
result = information; {
return NTStatus.STATUS_SUCCESS; result = null;
} return NTStatus.STATUS_NO_SUCH_FILE;
default:
result = null;
return NTStatus.STATUS_INVALID_INFO_CLASS;
} }
}
public static NTStatus GetFileInformation(out FileInformation result, FileSystemEntry entry, bool deletePending, FileInformationClass informationClass)
{
switch (informationClass) switch (informationClass)
{ {
case FileInformationClass.FileBasicInformation: case FileInformationClass.FileBasicInformation:
@ -55,10 +52,10 @@ namespace SMBLibrary.Server
case FileInformationClass.FileStandardInformation: case FileInformationClass.FileStandardInformation:
{ {
FileStandardInformation information = new FileStandardInformation(); FileStandardInformation information = new FileStandardInformation();
information.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); information.AllocationSize = (long)GetAllocationSize(entry.Size);
information.EndOfFile = (long)entry.Size; information.EndOfFile = (long)entry.Size;
information.Directory = entry.IsDirectory; information.Directory = entry.IsDirectory;
information.DeletePending = deletePending; information.DeletePending = fileHandle.DeleteOnClose;
result = information; result = information;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
@ -118,7 +115,7 @@ namespace SMBLibrary.Server
information.StandardInformation.AllocationSize = (long)GetAllocationSize(entry.Size); information.StandardInformation.AllocationSize = (long)GetAllocationSize(entry.Size);
information.StandardInformation.EndOfFile = (long)entry.Size; information.StandardInformation.EndOfFile = (long)entry.Size;
information.StandardInformation.Directory = entry.IsDirectory; information.StandardInformation.Directory = entry.IsDirectory;
information.StandardInformation.DeletePending = deletePending; information.StandardInformation.DeletePending = fileHandle.DeleteOnClose;
information.NameInformation.FileName = entry.Name; information.NameInformation.FileName = entry.Name;
result = information; result = information;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
@ -134,7 +131,7 @@ namespace SMBLibrary.Server
// A buffer of FileStreamInformation data elements is returned by the server. // A buffer of FileStreamInformation data elements is returned by the server.
FileStreamInformation information = new FileStreamInformation(); FileStreamInformation information = new FileStreamInformation();
information.StreamSize = (long)entry.Size; information.StreamSize = (long)entry.Size;
information.StreamAllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); information.StreamAllocationSize = (long)GetAllocationSize(entry.Size);
information.StreamName = "::$DATA"; information.StreamName = "::$DATA";
result = information; result = information;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
@ -166,7 +163,7 @@ namespace SMBLibrary.Server
information.LastAccessTime = entry.LastAccessTime; information.LastAccessTime = entry.LastAccessTime;
information.LastWriteTime = entry.LastWriteTime; information.LastWriteTime = entry.LastWriteTime;
information.ChangeTime = 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.EndOfFile = (long)entry.Size;
information.FileAttributes = GetFileAttributes(entry); information.FileAttributes = GetFileAttributes(entry);
result = information; result = information;

View file

@ -0,0 +1,261 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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
{
/// <param name="fileName">Expression as described in [MS-FSA] 2.1.4.4</param>
public NTStatus QueryDirectory(out List<QueryDirectoryFileInformation> 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<FileSystemEntry> 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<FileSystemEntry>();
entries.Add(entry);
}
try
{
result = FromFileSystemEntries(entries, informationClass);
}
catch (UnsupportedInformationLevelException)
{
return NTStatus.STATUS_INVALID_INFO_CLASS;
}
return NTStatus.STATUS_SUCCESS;
}
/// <param name="expression">Expression as described in [MS-FSA] 2.1.4.4</param>
private static List<FileSystemEntry> GetFiltered(List<FileSystemEntry> entries, string expression)
{
if (expression == "*")
{
return entries;
}
List<FileSystemEntry> result = new List<FileSystemEntry>();
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<QueryDirectoryFileInformation> FromFileSystemEntries(List<FileSystemEntry> entries, FileInformationClass informationClass)
{
List<QueryDirectoryFileInformation> result = new List<QueryDirectoryFileInformation>();
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();
}
}
}
}
}

View file

@ -9,11 +9,11 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using Utilities; 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) switch (informationClass)
{ {
@ -27,10 +27,10 @@ namespace SMBLibrary.Server
case FileSystemInformationClass.FileFsSizeInformation: case FileSystemInformationClass.FileFsSizeInformation:
{ {
FileFsSizeInformation information = new FileFsSizeInformation(); FileFsSizeInformation information = new FileFsSizeInformation();
information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize; information.TotalAllocationUnits = m_fileSystem.Size / ClusterSize;
information.AvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; information.AvailableAllocationUnits = m_fileSystem.FreeSpace / ClusterSize;
information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector; information.SectorsPerAllocationUnit = ClusterSize / BytesPerSector;
information.BytesPerSector = NTFileSystemHelper.BytesPerSector; information.BytesPerSector = BytesPerSector;
result = information; result = information;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
@ -47,7 +47,7 @@ namespace SMBLibrary.Server
FileFsAttributeInformation information = new FileFsAttributeInformation(); FileFsAttributeInformation information = new FileFsAttributeInformation();
information.FileSystemAttributes = FileSystemAttributes.UnicodeOnDisk; information.FileSystemAttributes = FileSystemAttributes.UnicodeOnDisk;
information.MaximumComponentNameLength = 255; information.MaximumComponentNameLength = 255;
information.FileSystemName = fileSystem.Name; information.FileSystemName = m_fileSystem.Name;
result = information; result = information;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
@ -63,11 +63,11 @@ namespace SMBLibrary.Server
case FileSystemInformationClass.FileFsFullSizeInformation: case FileSystemInformationClass.FileFsFullSizeInformation:
{ {
FileFsFullSizeInformation information = new FileFsFullSizeInformation(); FileFsFullSizeInformation information = new FileFsFullSizeInformation();
information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize; information.TotalAllocationUnits = m_fileSystem.Size / ClusterSize;
information.CallerAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; information.CallerAvailableAllocationUnits = m_fileSystem.FreeSpace / ClusterSize;
information.ActualAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize; information.ActualAvailableAllocationUnits = m_fileSystem.FreeSpace / ClusterSize;
information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector; information.SectorsPerAllocationUnit = ClusterSize / BytesPerSector;
information.BytesPerSector = NTFileSystemHelper.BytesPerSector; information.BytesPerSector = BytesPerSector;
result = information; result = information;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
@ -81,10 +81,10 @@ namespace SMBLibrary.Server
case FileSystemInformationClass.FileFsSectorSizeInformation: case FileSystemInformationClass.FileFsSectorSizeInformation:
{ {
FileFsSectorSizeInformation information = new FileFsSectorSizeInformation(); FileFsSectorSizeInformation information = new FileFsSectorSizeInformation();
information.LogicalBytesPerSector = NTFileSystemHelper.BytesPerSector; information.LogicalBytesPerSector = BytesPerSector;
information.PhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector; information.PhysicalBytesPerSectorForAtomicity = BytesPerSector;
information.PhysicalBytesPerSectorForPerformance = NTFileSystemHelper.BytesPerSector; information.PhysicalBytesPerSectorForPerformance = BytesPerSector;
information.FileSystemEffectivePhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector; information.FileSystemEffectivePhysicalBytesPerSectorForAtomicity = BytesPerSector;
information.ByteOffsetForSectorAlignment = 0; information.ByteOffsetForSectorAlignment = 0;
information.ByteOffsetForPartitionAlignment = 0; information.ByteOffsetForPartitionAlignment = 0;
result = information; result = information;

View file

@ -9,12 +9,13 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using Utilities; 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) if (information is FileBasicInformation)
{ {
FileBasicInformation basicInformation = (FileBasicInformation)information; FileBasicInformation basicInformation = (FileBasicInformation)information;
@ -23,23 +24,23 @@ namespace SMBLibrary.Server
bool isArchived = (basicInformation.FileAttributes & FileAttributes.Archive) > 0; bool isArchived = (basicInformation.FileAttributes & FileAttributes.Archive) > 0;
try try
{ {
fileSystem.SetAttributes(openFile.Path, isHidden, isReadonly, isArchived); m_fileSystem.SetAttributes(fileHandle.Path, isHidden, isReadonly, isArchived);
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
try 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) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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 status;
} }
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
@ -53,27 +54,27 @@ namespace SMBLibrary.Server
destination = @"\" + destination; 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. // Note: it's possible that we just want to upcase / downcase a filename letter.
try 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) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
openFile.Path = destination; fileHandle.Path = destination;
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else if (information is FileDispositionInformation) else if (information is FileDispositionInformation)
@ -81,20 +82,20 @@ namespace SMBLibrary.Server
if (((FileDispositionInformation)information).DeletePending) 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 // 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 try
{ {
state.LogToServer(Severity.Information, "SetFileInformation: Deleting file '{0}'", openFile.Path); Log(Severity.Information, "SetFileInformation: Deleting file '{0}'", fileHandle.Path);
fileSystem.Delete(openFile.Path); m_fileSystem.Delete(fileHandle.Path);
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
} }
@ -105,12 +106,12 @@ namespace SMBLibrary.Server
long allocationSize = ((FileAllocationInformation)information).AllocationSize; long allocationSize = ((FileAllocationInformation)information).AllocationSize;
try try
{ {
openFile.Stream.SetLength(allocationSize); fileHandle.Stream.SetLength(allocationSize);
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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 status;
} }
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;
@ -120,12 +121,12 @@ namespace SMBLibrary.Server
long endOfFile = ((FileEndOfFileInformation)information).EndOfFile; long endOfFile = ((FileEndOfFileInformation)information).EndOfFile;
try try
{ {
openFile.Stream.SetLength(endOfFile); fileHandle.Stream.SetLength(endOfFile);
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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 status;
} }
return NTStatus.STATUS_SUCCESS; return NTStatus.STATUS_SUCCESS;

View file

@ -7,23 +7,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using SMBLibrary.Services;
using Utilities; using Utilities;
namespace SMBLibrary.Server namespace SMBLibrary
{ {
/// <summary> public partial class NTFileSystemAdapter : INTFileStore
/// Helper class to access the FileSystemShare / NamedPipeShare in an NT-like manner dictated by the SMB protocol
/// </summary>
public partial class NTFileSystemHelper
{ {
public const int BytesPerSector = 512; private const int BytesPerSector = 512;
public const int ClusterSize = 4096; 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<LogEntry> 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; fileStatus = FileStatus.FILE_DOES_NOT_EXIST;
stream = null;
FileAccess createAccess = NTFileStoreHelper.ToCreateFileAccess(desiredAccess, createDisposition); FileAccess createAccess = NTFileStoreHelper.ToCreateFileAccess(desiredAccess, createDisposition);
bool requestedWriteAccess = (createAccess & FileAccess.Write) > 0; bool requestedWriteAccess = (createAccess & FileAccess.Write) > 0;
@ -35,7 +40,6 @@ namespace SMBLibrary.Server
createDisposition != CreateDisposition.FILE_OPEN_IF && createDisposition != CreateDisposition.FILE_OPEN_IF &&
createDisposition != CreateDisposition.FILE_SUPERSEDE)) createDisposition != CreateDisposition.FILE_SUPERSEDE))
{ {
entry = null;
return NTStatus.STATUS_INVALID_PARAMETER; return NTStatus.STATUS_INVALID_PARAMETER;
} }
@ -43,19 +47,18 @@ namespace SMBLibrary.Server
if (path.Contains(":")) if (path.Contains(":"))
{ {
// Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND
entry = null;
return NTStatus.STATUS_NO_SUCH_FILE; return NTStatus.STATUS_NO_SUCH_FILE;
} }
FileSystemEntry entry;
try try
{ {
entry = fileSystem.GetEntry(path); entry = m_fileSystem.GetEntry(path);
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(ex); NTStatus status = ToNTStatus(ex);
state.LogToServer(Severity.Debug, "CreateFile: Error retrieving '{0}'. {1}.", path, status); Log(Severity.Debug, "CreateFile: Error retrieving '{0}'. {1}.", path, status);
entry = null;
return status; return status;
} }
@ -82,7 +85,7 @@ namespace SMBLibrary.Server
if (entry != null) if (entry != null)
{ {
// File already exists, fail the request // 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; fileStatus = FileStatus.FILE_EXISTS;
return NTStatus.STATUS_OBJECT_NAME_COLLISION; return NTStatus.STATUS_OBJECT_NAME_COLLISION;
} }
@ -96,19 +99,19 @@ namespace SMBLibrary.Server
{ {
if (forceDirectory) if (forceDirectory)
{ {
state.LogToServer(Severity.Information, "CreateFile: Creating directory '{0}'", path); Log(Severity.Information, "CreateFile: Creating directory '{0}'", path);
entry = fileSystem.CreateDirectory(path); entry = m_fileSystem.CreateDirectory(path);
} }
else else
{ {
state.LogToServer(Severity.Information, "CreateFile: Creating file '{0}'", path); Log(Severity.Information, "CreateFile: Creating file '{0}'", path);
entry = fileSystem.CreateFile(path); entry = m_fileSystem.CreateFile(path);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
fileStatus = FileStatus.FILE_CREATED; fileStatus = FileStatus.FILE_CREATED;
@ -134,19 +137,19 @@ namespace SMBLibrary.Server
{ {
if (forceDirectory) if (forceDirectory)
{ {
state.LogToServer(Severity.Information, "CreateFile: Creating directory '{0}'", path); Log(Severity.Information, "CreateFile: Creating directory '{0}'", path);
entry = fileSystem.CreateDirectory(path); entry = m_fileSystem.CreateDirectory(path);
} }
else else
{ {
state.LogToServer(Severity.Information, "CreateFile: Creating file '{0}'", path); Log(Severity.Information, "CreateFile: Creating file '{0}'", path);
entry = fileSystem.CreateFile(path); entry = m_fileSystem.CreateFile(path);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
fileStatus = FileStatus.FILE_CREATED; fileStatus = FileStatus.FILE_CREATED;
@ -165,13 +168,13 @@ namespace SMBLibrary.Server
// Truncate the file // Truncate the file
try 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(); temp.Close();
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
fileStatus = FileStatus.FILE_OVERWRITTEN; fileStatus = FileStatus.FILE_OVERWRITTEN;
@ -181,12 +184,12 @@ namespace SMBLibrary.Server
// Delete the old file // Delete the old file
try try
{ {
fileSystem.Delete(path); m_fileSystem.Delete(path);
} }
catch(Exception ex) catch(Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
@ -194,19 +197,19 @@ namespace SMBLibrary.Server
{ {
if (forceDirectory) if (forceDirectory)
{ {
state.LogToServer(Severity.Information, "CreateFile: Creating directory '{0}'", path); Log(Severity.Information, "CreateFile: Creating directory '{0}'", path);
entry = fileSystem.CreateDirectory(path); entry = m_fileSystem.CreateDirectory(path);
} }
else else
{ {
state.LogToServer(Severity.Information, "CreateFile: Creating file '{0}'", path); Log(Severity.Information, "CreateFile: Creating file '{0}'", path);
entry = fileSystem.CreateFile(path); entry = m_fileSystem.CreateFile(path);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
fileStatus = FileStatus.FILE_SUPERSEDED; fileStatus = FileStatus.FILE_SUPERSEDED;
@ -219,6 +222,7 @@ namespace SMBLibrary.Server
} }
FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(desiredAccess.File); FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(desiredAccess.File);
Stream stream;
bool deleteOnClose = false; bool deleteOnClose = false;
if (fileAccess == (FileAccess)0 || entry.IsDirectory) if (fileAccess == (FileAccess)0 || entry.IsDirectory)
{ {
@ -227,13 +231,14 @@ namespace SMBLibrary.Server
else else
{ {
deleteOnClose = (createOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0; 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) if (openStatus != NTStatus.STATUS_SUCCESS)
{ {
return openStatus; return openStatus;
} }
} }
handle = new FileHandle(path, entry.IsDirectory, stream, deleteOnClose);
if (fileStatus != FileStatus.FILE_CREATED && if (fileStatus != FileStatus.FILE_CREATED &&
fileStatus != FileStatus.FILE_OVERWRITTEN && fileStatus != FileStatus.FILE_OVERWRITTEN &&
fileStatus != FileStatus.FILE_SUPERSEDED) fileStatus != FileStatus.FILE_SUPERSEDED)
@ -243,7 +248,7 @@ namespace SMBLibrary.Server
return NTStatus.STATUS_SUCCESS; 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; stream = null;
// When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point. // 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 disableBuffering = (openOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
bool buffered = (openOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint; bool buffered = (openOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
FileShare fileShare = NTFileStoreHelper.ToFileShare(shareAccess); 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 try
{ {
stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare); stream = m_fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare);
} }
catch (Exception ex) catch (Exception ex)
{ {
NTStatus status = ToNTStatus(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; return status;
} }
@ -273,86 +278,111 @@ namespace SMBLibrary.Server
return NTStatus.STATUS_SUCCESS; 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; FileHandle fileHandle = (FileHandle)handle;
string openFilePath = openFile.Path; if (fileHandle.Stream != null)
Stream stream = openFile.Stream;
if (stream is RPCPipeStream)
{ {
data = new byte[maxCount]; fileHandle.Stream.Close();
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 || !stream.CanRead)
{
state.LogToServer(Severity.Debug, "ReadFile: Cannot read '{0}', Invalid Operation.", openFilePath);
return NTStatus.STATUS_ACCESS_DENIED;
}
int bytesRead; if (fileHandle.DeleteOnClose)
{
try try
{ {
stream.Seek(offset, SeekOrigin.Begin); m_fileSystem.Delete(fileHandle.Path);
data = new byte[maxCount];
bytesRead = stream.Read(data, 0, maxCount);
} }
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) public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount)
{ {
// EOF, we must trim the response data array data = null;
data = ByteReader.ReadBytes(data, 0, bytesRead); FileHandle fileHandle = (FileHandle)handle;
} string path = fileHandle.Path;
return NTStatus.STATUS_SUCCESS; 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<LogEntry> 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; Log(severity, String.Format(message, args));
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;
}
} }
/// <param name="exception">IFileSystem exception</param> /// <param name="exception">IFileSystem exception</param>

View file

@ -0,0 +1,37 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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
{
/// <summary>
/// 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.
/// </summary>
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<QueryDirectoryFileInformation> 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);
}
}

View file

@ -98,5 +98,36 @@ namespace SMBLibrary
return result; 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;
}
} }
} }

View file

@ -0,0 +1,150 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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<RemoteService> m_services;
public NamedPipeStore(List<RemoteService> 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<QueryDirectoryFileInformation> 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;
}
}
}

View file

@ -77,7 +77,13 @@
<Compile Include="NetBios\SessionPackets\SessionPacket.cs" /> <Compile Include="NetBios\SessionPackets\SessionPacket.cs" />
<Compile Include="NetBios\SessionPackets\SessionRequestPacket.cs" /> <Compile Include="NetBios\SessionPackets\SessionRequestPacket.cs" />
<Compile Include="NetBios\SessionPackets\SessionRetargetResponsePacket.cs" /> <Compile Include="NetBios\SessionPackets\SessionRetargetResponsePacket.cs" />
<Compile Include="NTFileStore\Adapter\FileHandle.cs" />
<Compile Include="NTFileStore\Adapter\IOExceptionHelper.cs" /> <Compile Include="NTFileStore\Adapter\IOExceptionHelper.cs" />
<Compile Include="NTFileStore\Adapter\NTFileSystemAdapter.cs" />
<Compile Include="NTFileStore\Adapter\NTFileSystemAdapter.Query.cs" />
<Compile Include="NTFileStore\Adapter\NTFileSystemAdapter.QueryDirectory.cs" />
<Compile Include="NTFileStore\Adapter\NTFileSystemAdapter.QueryFileSystem.cs" />
<Compile Include="NTFileStore\Adapter\NTFileSystemAdapter.Set.cs" />
<Compile Include="NTFileStore\Enums\FileInformation\CompressionFormat.cs" /> <Compile Include="NTFileStore\Enums\FileInformation\CompressionFormat.cs" />
<Compile Include="NTFileStore\Enums\FileInformation\FileAttributes.cs" /> <Compile Include="NTFileStore\Enums\FileInformation\FileAttributes.cs" />
<Compile Include="NTFileStore\Enums\FileInformation\FileInformationClass.cs" /> <Compile Include="NTFileStore\Enums\FileInformation\FileInformationClass.cs" />
@ -91,6 +97,8 @@
<Compile Include="NTFileStore\Enums\NtCreateFile\CreateOptions.cs" /> <Compile Include="NTFileStore\Enums\NtCreateFile\CreateOptions.cs" />
<Compile Include="NTFileStore\Enums\NtCreateFile\FileStatus.cs" /> <Compile Include="NTFileStore\Enums\NtCreateFile\FileStatus.cs" />
<Compile Include="NTFileStore\Enums\NtCreateFile\ShareAccess.cs" /> <Compile Include="NTFileStore\Enums\NtCreateFile\ShareAccess.cs" />
<Compile Include="NTFileStore\INTFileStore.cs" />
<Compile Include="NTFileStore\NamedPipeStore.cs" />
<Compile Include="NTFileStore\NTFileStoreHelper.cs" /> <Compile Include="NTFileStore\NTFileStoreHelper.cs" />
<Compile Include="NTFileStore\Structures\ACE\AccessAllowedACE.cs" /> <Compile Include="NTFileStore\Structures\ACE\AccessAllowedACE.cs" />
<Compile Include="NTFileStore\Structures\ACE\ACE.cs" /> <Compile Include="NTFileStore\Structures\ACE\ACE.cs" />
@ -174,11 +182,6 @@
<Compile Include="Server\Exceptions\EmptyPasswordNotAllowedException.cs" /> <Compile Include="Server\Exceptions\EmptyPasswordNotAllowedException.cs" />
<Compile Include="Server\Exceptions\InvalidRequestException.cs" /> <Compile Include="Server\Exceptions\InvalidRequestException.cs" />
<Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" /> <Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.Find.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.Query.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.QueryFileSystem.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.Set.cs" />
<Compile Include="Server\Helpers\ServerPathUtils.cs" /> <Compile Include="Server\Helpers\ServerPathUtils.cs" />
<Compile Include="Server\IndependentUserCollection.cs" /> <Compile Include="Server\IndependentUserCollection.cs" />
<Compile Include="Server\INTLMAuthenticationProvider.cs" /> <Compile Include="Server\INTLMAuthenticationProvider.cs" />
@ -195,10 +198,11 @@
<Compile Include="Server\SMB1\ReadWriteResponseHelper.cs" /> <Compile Include="Server\SMB1\ReadWriteResponseHelper.cs" />
<Compile Include="Server\SMB1\ServerResponseHelper.cs" /> <Compile Include="Server\SMB1\ServerResponseHelper.cs" />
<Compile Include="Server\SMB1\SessionSetupHelper.cs" /> <Compile Include="Server\SMB1\SessionSetupHelper.cs" />
<Compile Include="Server\SMB1\SMB1FileSystemHelper.Find.cs" /> <Compile Include="Server\SMB1\SMB1FileStoreHelper.cs" />
<Compile Include="Server\SMB1\SMB1FileSystemHelper.Query.cs" /> <Compile Include="Server\SMB1\SMB1FileStoreHelper.Query.cs" />
<Compile Include="Server\SMB1\SMB1FileSystemHelper.QueryFileSystem.cs" /> <Compile Include="Server\SMB1\SMB1FileStoreHelper.QueryDirectory.cs" />
<Compile Include="Server\SMB1\SMB1FileSystemHelper.Set.cs" /> <Compile Include="Server\SMB1\SMB1FileStoreHelper.QueryFileSystem.cs" />
<Compile Include="Server\SMB1\SMB1FileStoreHelper.Set.cs" />
<Compile Include="Server\SMB1\Transaction2SubcommandHelper.cs" /> <Compile Include="Server\SMB1\Transaction2SubcommandHelper.cs" />
<Compile Include="Server\SMB1\TransactionHelper.cs" /> <Compile Include="Server\SMB1\TransactionHelper.cs" />
<Compile Include="Server\SMB1\TransactionSubcommandHelper.cs" /> <Compile Include="Server\SMB1\TransactionSubcommandHelper.cs" />

View file

@ -14,14 +14,12 @@ namespace SMBLibrary.Server
public class OpenFileObject public class OpenFileObject
{ {
public string Path; public string Path;
public Stream Stream; public object Handle;
public bool DeleteOnClose;
public OpenFileObject(string path, Stream stream, bool deleteOnClose) public OpenFileObject(string path, object handle)
{ {
Path = path; Path = path;
Stream = stream; Handle = handle;
DeleteOnClose = deleteOnClose;
} }
} }
} }

View file

@ -12,10 +12,10 @@ namespace SMBLibrary.Server
{ {
public class OpenSearch public class OpenSearch
{ {
public List<FileSystemEntry> Entries; public List<QueryDirectoryFileInformation> Entries;
public int EnumerationLocation; public int EnumerationLocation;
public OpenSearch(List<FileSystemEntry> entries, int enumerationLocation) public OpenSearch(List<QueryDirectoryFileInformation> entries, int enumerationLocation)
{ {
Entries = entries; Entries = entries;
EnumerationLocation = enumerationLocation; EnumerationLocation = enumerationLocation;

View file

@ -70,17 +70,12 @@ namespace SMBLibrary.Server
return AddOpenFile(relativePath, null); return AddOpenFile(relativePath, null);
} }
public ushort? AddOpenFile(string relativePath, Stream stream) public ushort? AddOpenFile(string relativePath, object handle)
{
return AddOpenFile(relativePath, stream, false);
}
public ushort? AddOpenFile(string relativePath, Stream stream, bool deleteOnClose)
{ {
ushort? fileID = m_connection.AllocateFileID(); ushort? fileID = m_connection.AllocateFileID();
if (fileID.HasValue) if (fileID.HasValue)
{ {
m_openFiles.Add(fileID.Value, new OpenFileObject(relativePath, stream, deleteOnClose)); m_openFiles.Add(fileID.Value, new OpenFileObject(relativePath, handle));
} }
return fileID; return fileID;
} }
@ -94,11 +89,6 @@ namespace SMBLibrary.Server
public void RemoveOpenFile(ushort fileID) public void RemoveOpenFile(ushort fileID)
{ {
Stream stream = m_openFiles[fileID].Stream;
if (stream != null)
{
stream.Close();
}
m_openFiles.Remove(fileID); m_openFiles.Remove(fileID);
} }
@ -120,7 +110,7 @@ namespace SMBLibrary.Server
return null; return null;
} }
public ushort? AddOpenSearch(List<FileSystemEntry> entries, int enumerationLocation) public ushort? AddOpenSearch(List<QueryDirectoryFileInformation> entries, int enumerationLocation)
{ {
ushort? searchHandle = AllocateSearchHandle(); ushort? searchHandle = AllocateSearchHandle();
if (searchHandle.HasValue) if (searchHandle.HasValue)

View file

@ -90,24 +90,13 @@ namespace SMBLibrary.Server
return m_connectedTrees.ContainsKey(treeID); return m_connectedTrees.ContainsKey(treeID);
} }
/// <param name="relativePath">Should include the path relative to the share</param>
/// <returns>The persistent portion of the FileID</returns> /// <returns>The persistent portion of the FileID</returns>
public ulong? AddOpenFile(string relativePath) public ulong? AddOpenFile(string relativePath, object handle)
{
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)
{ {
ulong? persistentID = m_connection.AllocatePersistentFileID(); ulong? persistentID = m_connection.AllocatePersistentFileID();
if (persistentID.HasValue) if (persistentID.HasValue)
{ {
m_openFiles.Add(persistentID.Value, new OpenFileObject(relativePath, stream, deleteOnClose)); m_openFiles.Add(persistentID.Value, new OpenFileObject(relativePath, handle));
} }
return persistentID; return persistentID;
} }
@ -126,16 +115,11 @@ namespace SMBLibrary.Server
public void RemoveOpenFile(ulong fileID) public void RemoveOpenFile(ulong fileID)
{ {
Stream stream = m_openFiles[fileID].Stream;
if (stream != null)
{
stream.Close();
}
m_openFiles.Remove(fileID); m_openFiles.Remove(fileID);
m_openSearches.Remove(fileID); m_openSearches.Remove(fileID);
} }
public OpenSearch AddOpenSearch(ulong fileID, List<FileSystemEntry> entries, int enumerationLocation) public OpenSearch AddOpenSearch(ulong fileID, List<QueryDirectoryFileInformation> entries, int enumerationLocation)
{ {
OpenSearch openSearch = new OpenSearch(entries, enumerationLocation); OpenSearch openSearch = new OpenSearch(entries, enumerationLocation);
m_openSearches.Add(fileID, openSearch); m_openSearches.Add(fileID, openSearch);

View file

@ -1,155 +0,0 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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
{
/// <param name="fileName">Expression as described in [MS-FSA] 2.1.4.4</param>
public static NTStatus FindEntries(out List<FileSystemEntry> 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<FileSystemEntry>();
entries.Add(entry);
}
return NTStatus.STATUS_SUCCESS;
}
/// <param name="expression">Expression as described in [MS-FSA] 2.1.4.4</param>
private static List<FileSystemEntry> GetFiltered(List<FileSystemEntry> entries, string expression)
{
if (expression == "*")
{
return entries;
}
List<FileSystemEntry> result = new List<FileSystemEntry>();
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;
}
}
}

View file

@ -23,22 +23,10 @@ namespace SMBLibrary.Server.SMB1
header.Status = NTStatus.STATUS_ACCESS_DENIED; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); 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); return new ErrorResponse(request.CommandName);
} }
@ -53,51 +41,13 @@ namespace SMBLibrary.Server.SMB1
header.Status = NTStatus.STATUS_ACCESS_DENIED; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
IFileSystem fileSystem = share.FileSystem;
FileSystemEntry entry = fileSystem.GetEntry(request.DirectoryName); header.Status = SMB1FileStoreHelper.DeleteDirectory(share.FileStore, request.DirectoryName);
if (entry == null) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
header.Status = NTStatus.STATUS_NO_SUCH_FILE;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
return new DeleteDirectoryResponse();
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();
} }
internal static SMB1Command GetDeleteResponse(SMB1Header header, DeleteRequest request, FileSystemShare share, SMB1ConnectionState state) 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; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
IFileSystem fileSystem = share.FileSystem;
FileSystemEntry entry = fileSystem.GetEntry(request.FileName); // [MS-CIFS] This command cannot delete directories or volumes.
if (entry == null) 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 ErrorResponse(request.CommandName);
} }
return new DeleteResponse();
} }
internal static SMB1Command GetRenameResponse(SMB1Header header, RenameRequest request, FileSystemShare share, SMB1ConnectionState state) 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; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
IFileSystem fileSystem = share.FileSystem;
FileSystemEntry sourceEntry = fileSystem.GetEntry(request.OldFileName); header.Status = SMB1FileStoreHelper.Rename(share.FileStore, request.OldFileName, request.NewFileName, request.SearchAttributes);
if (sourceEntry == null) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
header.Status = NTStatus.STATUS_NO_SUCH_FILE;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
return new RenameResponse();
}
// The file must not already exist unless we just want to upcase / downcase a filename letter internal static SMB1Command GetCheckDirectoryResponse(SMB1Header header, CheckDirectoryRequest request, FileSystemShare share, SMB1ConnectionState state)
FileSystemEntry destinationEntry = fileSystem.GetEntry(request.NewFileName); {
if (destinationEntry != null && SMB1Session session = state.GetSession(header.UID);
!String.Equals(request.OldFileName, request.NewFileName, StringComparison.InvariantCultureIgnoreCase)) 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; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); 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; SMB1Session session = state.GetSession(header.UID);
FileSystemEntry entry = fileSystem.GetEntry(request.FileName); if (!share.HasReadAccess(session.UserName, request.FileName, state.ClientEndPoint))
if (entry == null) {
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); return new ErrorResponse(request.CommandName);
} }
QueryInformationResponse response = new QueryInformationResponse(); QueryInformationResponse response = new QueryInformationResponse();
response.FileAttributes = SMB1FileSystemHelper.GetFileAttributes(entry); response.FileAttributes = SMB1FileStoreHelper.GetFileAttributes(fileInfo.FileAttributes);
response.LastWriteTime = entry.LastWriteTime; response.LastWriteTime = fileInfo.LastWriteTime;
response.FileSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); response.FileSize = (uint)Math.Min(UInt32.MaxValue, fileInfo.EndOfFile);
return response; return response;
} }
@ -220,37 +139,13 @@ namespace SMBLibrary.Server.SMB1
header.Status = NTStatus.STATUS_ACCESS_DENIED; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
IFileSystem fileSystem = share.FileSystem;
FileSystemEntry entry = fileSystem.GetEntry(request.FileName); header.Status = SMB1FileStoreHelper.SetInformation(share.FileStore, request.FileName, request.FileAttributes, request.LastWriteTime);
if (entry == null) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
header.Status = NTStatus.STATUS_NO_SUCH_FILE;
return new ErrorResponse(request.CommandName); 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(); return new SetInformationResponse();
} }
@ -269,9 +164,13 @@ namespace SMBLibrary.Server.SMB1
header.Status = NTStatus.STATUS_ACCESS_DENIED; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return new ErrorResponse(request.CommandName); 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(); return new SetInformation2Response();
} }
} }

View file

@ -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) if (share is NamedPipeShare)
{ {
Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path); if (isExtended)
if (pipeStream != null)
{ {
ushort? fileID = session.AddOpenFile(path, pipeStream); return CreateResponseExtendedForNamedPipe(fileID.Value, FileStatus.FILE_OPENED);
if (!fileID.HasValue) }
{ else
header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES; {
return new ErrorResponse(request.CommandName); return CreateResponseForNamedPipe(fileID.Value, FileStatus.FILE_OPENED);
}
if (isExtended)
{
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 else // FileSystemShare
{ {
FileSystemShare fileSystemShare = (FileSystemShare)share; FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle);
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);
}
if (isExtended) 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) if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0)
{ {
response.OpLockLevel = OpLockLevel.BatchOpLockGranted; response.OpLockLevel = OpLockLevel.BatchOpLockGranted;
@ -93,7 +73,7 @@ namespace SMBLibrary.Server.SMB1
} }
else else
{ {
NTCreateAndXResponse response = CreateResponseFromFileSystemEntry(entry, fileID.Value, fileStatus); NTCreateAndXResponse response = CreateResponseFromFileInformation(fileInfo, fileID.Value, fileStatus);
if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0) if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0)
{ {
response.OpLockLevel = OpLockLevel.BatchOpLockGranted; response.OpLockLevel = OpLockLevel.BatchOpLockGranted;
@ -140,52 +120,38 @@ namespace SMBLibrary.Server.SMB1
return response; 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(); NTCreateAndXResponse response = new NTCreateAndXResponse();
if (entry.IsDirectory)
{
response.ExtFileAttributes = ExtendedFileAttributes.Directory;
response.Directory = true;
}
else
{
response.ExtFileAttributes = ExtendedFileAttributes.Normal;
}
response.FID = fileID; response.FID = fileID;
response.CreateDisposition = ToCreateDisposition(fileStatus); response.CreateDisposition = ToCreateDisposition(fileStatus);
response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); response.CreateTime = fileInfo.CreationTime;
response.EndOfFile = (long)entry.Size; response.LastAccessTime = fileInfo.LastAccessTime;
response.CreateTime = entry.CreationTime; response.LastWriteTime = fileInfo.LastWriteTime;
response.LastAccessTime = entry.LastAccessTime; response.LastChangeTime = fileInfo.LastWriteTime;
response.LastWriteTime = entry.LastWriteTime; response.AllocationSize = fileInfo.AllocationSize;
response.LastChangeTime = entry.LastWriteTime; response.EndOfFile = fileInfo.EndOfFile;
response.ExtFileAttributes = (ExtendedFileAttributes)fileInfo.FileAttributes;
response.ResourceType = ResourceType.FileTypeDisk; response.ResourceType = ResourceType.FileTypeDisk;
response.Directory = fileInfo.IsDirectory;
return response; 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(); NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended();
if (entry.IsDirectory)
{
response.ExtFileAttributes = ExtendedFileAttributes.Directory;
response.Directory = true;
}
else
{
response.ExtFileAttributes = ExtendedFileAttributes.Normal;
}
response.FID = fileID; response.FID = fileID;
response.CreateDisposition = ToCreateDisposition(fileStatus); response.CreateDisposition = ToCreateDisposition(fileStatus);
response.CreateTime = entry.CreationTime; response.CreateTime = fileInfo.CreationTime;
response.LastAccessTime = entry.LastAccessTime; response.LastAccessTime = fileInfo.LastAccessTime;
response.LastWriteTime = entry.LastWriteTime; response.LastWriteTime = fileInfo.LastWriteTime;
response.LastChangeTime = entry.LastWriteTime; response.LastChangeTime = fileInfo.LastWriteTime;
response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); response.ExtFileAttributes = (ExtendedFileAttributes)fileInfo.FileAttributes;
response.EndOfFile = (long)entry.Size; response.AllocationSize = fileInfo.AllocationSize;
response.EndOfFile = fileInfo.EndOfFile;
response.ResourceType = ResourceType.FileTypeDisk; response.ResourceType = ResourceType.FileTypeDisk;
response.FileStatusFlags = FileStatusFlags.NO_EAS | FileStatusFlags.NO_SUBSTREAMS | FileStatusFlags.NO_REPARSETAG; 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 | 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_READ_EA | FileAccessMask.FILE_WRITE_EA |
FileAccessMask.FILE_EXECUTE | FileAccessMask.FILE_EXECUTE |

View file

@ -48,38 +48,18 @@ namespace SMBLibrary.Server.SMB1
} }
} }
FileSystemEntry entry = null; object handle;
Stream stream = null;
FileStatus fileStatus; 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); return new ErrorResponse(request.CommandName);
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);
}
} }
ushort? fileID = session.AddOpenFile(path, stream); ushort? fileID = session.AddOpenFile(path, handle);
if (!fileID.HasValue) if (!fileID.HasValue)
{ {
if (stream != null) share.FileStore.CloseFile(handle);
{
stream.Close();
}
header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES; header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
@ -98,13 +78,14 @@ namespace SMBLibrary.Server.SMB1
} }
else // FileSystemShare else // FileSystemShare
{ {
FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle);
if (isExtended) if (isExtended)
{ {
return CreateResponseExtendedFromFileSystemEntry(entry, fileID.Value, openResult); return CreateResponseExtendedFromFileInfo(fileInfo, fileID.Value, openResult);
} }
else else
{ {
return CreateResponseFromFileSystemEntry(entry, fileID.Value, openResult); return CreateResponseFromFileInfo(fileInfo, fileID.Value, openResult);
} }
} }
} }
@ -288,40 +269,26 @@ namespace SMBLibrary.Server.SMB1
return response; 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(); OpenAndXResponse response = new OpenAndXResponse();
response.FID = fileID; response.FID = fileID;
if (entry.IsDirectory) response.FileAttrs = SMB1FileStoreHelper.GetFileAttributes(fileInfo.FileAttributes);
{ response.LastWriteTime = fileInfo.LastWriteTime;
response.FileAttrs = SMBFileAttributes.Directory; response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, fileInfo.EndOfFile);
}
else
{
response.FileAttrs = SMBFileAttributes.Normal;
}
response.LastWriteTime = entry.LastWriteTime;
response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, entry.Size);
response.AccessRights = AccessRights.SMB_DA_ACCESS_READ; response.AccessRights = AccessRights.SMB_DA_ACCESS_READ;
response.ResourceType = ResourceType.FileTypeDisk; response.ResourceType = ResourceType.FileTypeDisk;
response.OpenResults.OpenResult = openResult; response.OpenResults.OpenResult = openResult;
return response; 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(); OpenAndXResponseExtended response = new OpenAndXResponseExtended();
response.FID = fileID; response.FID = fileID;
if (entry.IsDirectory) response.FileAttrs = SMB1FileStoreHelper.GetFileAttributes(fileInfo.FileAttributes);
{ response.LastWriteTime = fileInfo.LastWriteTime;
response.FileAttrs = SMBFileAttributes.Directory; response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, fileInfo.EndOfFile);
}
else
{
response.FileAttrs = SMBFileAttributes.Normal;
}
response.LastWriteTime = entry.LastWriteTime;
response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, entry.Size);
response.AccessRights = AccessRights.SMB_DA_ACCESS_READ; response.AccessRights = AccessRights.SMB_DA_ACCESS_READ;
response.ResourceType = ResourceType.FileTypeDisk; response.ResourceType = ResourceType.FileTypeDisk;
response.OpenResults.OpenResult = openResult; response.OpenResults.OpenResult = openResult;

View file

@ -37,7 +37,7 @@ namespace SMBLibrary.Server.SMB1
} }
byte[] data; 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) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
@ -74,7 +74,7 @@ namespace SMBLibrary.Server.SMB1
maxCount = request.MaxCountLarge; maxCount = request.MaxCountLarge;
} }
byte[] data; 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) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
@ -110,7 +110,7 @@ namespace SMBLibrary.Server.SMB1
} }
int numberOfBytesWritten; 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) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
@ -140,7 +140,7 @@ namespace SMBLibrary.Server.SMB1
} }
int numberOfBytesWritten; 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) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);

View file

@ -0,0 +1,200 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}
}
}

View file

@ -0,0 +1,139 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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)
/// <param name="fileNamePattern">The filename pattern to search for. This field MAY contain wildcard characters</param>
/// <exception cref="System.UnauthorizedAccessException"></exception>
public static NTStatus QueryDirectory(out List<QueryDirectoryFileInformation> 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;
}
}
/// <exception cref="SMBLibrary.UnsupportedInformationLevelException"></exception>
public static FindInformationList GetFindInformationList(List<QueryDirectoryFileInformation> 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;
}
/// <exception cref="SMBLibrary.UnsupportedInformationLevelException"></exception>
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();
}
}
}
}
}

View file

@ -0,0 +1,99 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}
}
}

View file

@ -0,0 +1,58 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}
}

View file

@ -0,0 +1,184 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}

View file

@ -1,116 +0,0 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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)
/// <param name="fileNamePattern">The filename pattern to search for. This field MAY contain wildcard characters</param>
/// <exception cref="System.UnauthorizedAccessException"></exception>
public static NTStatus FindEntries(out List<FileSystemEntry> 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;
}
}
/// <exception cref="SMBLibrary.UnsupportedInformationLevelException"></exception>
public static FindInformationList GetFindInformationList(List<FileSystemEntry> 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;
}
/// <exception cref="SMBLibrary.UnsupportedInformationLevelException"></exception>
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();
}
}
}
}
}

View file

@ -1,155 +0,0 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}

View file

@ -1,63 +0,0 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}
}
}

View file

@ -1,163 +0,0 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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;
}
}
}
}

View file

@ -26,20 +26,14 @@ namespace SMBLibrary.Server.SMB1
} }
state.LogToServer(Severity.Verbose, "Close: Closing file '{0}'", openFile.Path); state.LogToServer(Severity.Verbose, "Close: Closing file '{0}'", openFile.Path);
session.RemoveOpenFile(request.FID); header.Status = share.FileStore.CloseFile(openFile.Handle);
if (openFile.DeleteOnClose && share is FileSystemShare) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
try return new ErrorResponse(request.CommandName);
{
((FileSystemShare)share).FileSystem.Delete(openFile.Path);
}
catch
{
state.LogToServer(Severity.Debug, "Close: Cannot delete '{0}'", openFile.Path);
}
} }
CloseResponse response = new CloseResponse();
return response; session.RemoveOpenFile(request.FID);
return new CloseResponse();
} }
internal static SMB1Command GetFindClose2Request(SMB1Header header, FindClose2Request request, SMB1ConnectionState state) internal static SMB1Command GetFindClose2Request(SMB1Header header, FindClose2Request request, SMB1ConnectionState state)

View file

@ -18,11 +18,21 @@ namespace SMBLibrary.Server.SMB1
internal static Transaction2FindFirst2Response GetSubcommandResponse(SMB1Header header, Transaction2FindFirst2Request subcommand, FileSystemShare share, SMB1ConnectionState state) internal static Transaction2FindFirst2Response GetSubcommandResponse(SMB1Header header, Transaction2FindFirst2Request subcommand, FileSystemShare share, SMB1ConnectionState state)
{ {
SMB1Session session = state.GetSession(header.UID); SMB1Session session = state.GetSession(header.UID);
IFileSystem fileSystem = share.FileSystem;
string fileNamePattern = subcommand.FileName; string fileNamePattern = subcommand.FileName;
List<FileSystemEntry> entries; List<QueryDirectoryFileInformation> entries;
NTStatus searchStatus = SMB1FileSystemHelper.FindEntries(out entries, fileSystem, fileNamePattern); 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) if (searchStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', NTStatus: {1}", fileNamePattern, searchStatus.ToString()); 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; bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0;
int entriesToReturn = Math.Min(subcommand.SearchCount, entries.Count); int entriesToReturn = Math.Min(subcommand.SearchCount, entries.Count);
List<FileSystemEntry> segment = entries.GetRange(0, entriesToReturn); List<QueryDirectoryFileInformation> segment = entries.GetRange(0, entriesToReturn);
int maxLength = (int)state.GetMaxDataCount(header.PID).Value; int maxLength = (int)state.GetMaxDataCount(header.PID).Value;
FindInformationList findInformationList; FindInformationList findInformationList = SMB1FileStoreHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength);
try
{
findInformationList = SMB1FileSystemHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength);
}
catch (UnsupportedInformationLevelException)
{
header.Status = NTStatus.STATUS_OS2_INVALID_LEVEL;
return null;
}
int returnCount = findInformationList.Count; int returnCount = findInformationList.Count;
Transaction2FindFirst2Response response = new Transaction2FindFirst2Response(); Transaction2FindFirst2Response response = new Transaction2FindFirst2Response();
response.SetFindInformationList(findInformationList, header.UnicodeFlag); response.SetFindInformationList(findInformationList, header.UnicodeFlag);
@ -91,11 +92,11 @@ namespace SMBLibrary.Server.SMB1
bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0; bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0;
int maxLength = (int)state.GetMaxDataCount(header.PID).Value; int maxLength = (int)state.GetMaxDataCount(header.PID).Value;
int maxCount = Math.Min(openSearch.Entries.Count - openSearch.EnumerationLocation, subcommand.SearchCount); int maxCount = Math.Min(openSearch.Entries.Count - openSearch.EnumerationLocation, subcommand.SearchCount);
List<FileSystemEntry> segment = openSearch.Entries.GetRange(openSearch.EnumerationLocation, maxCount); List<QueryDirectoryFileInformation> segment = openSearch.Entries.GetRange(openSearch.EnumerationLocation, maxCount);
FindInformationList findInformationList; FindInformationList findInformationList;
try try
{ {
findInformationList = SMB1FileSystemHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength); findInformationList = SMB1FileStoreHelper.GetFindInformationList(segment, subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys, maxLength);
} }
catch (UnsupportedInformationLevelException) catch (UnsupportedInformationLevelException)
{ {
@ -125,7 +126,7 @@ namespace SMBLibrary.Server.SMB1
Transaction2QueryFSInformationResponse response = new Transaction2QueryFSInformationResponse(); Transaction2QueryFSInformationResponse response = new Transaction2QueryFSInformationResponse();
QueryFSInformation queryFSInformation; 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) if (queryStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information level: {0}, NTStatus: {1}", subcommand.InformationLevel, queryStatus); 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; header.Status = NTStatus.STATUS_ACCESS_DENIED;
return null; 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(); Transaction2QueryPathInformationResponse response = new Transaction2QueryPathInformationResponse();
QueryInformation queryInformation; 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) if (queryStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information level: {1}, NTStatus: {2}", path, subcommand.InformationLevel, queryStatus); 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) internal static Transaction2QueryFileInformationResponse GetSubcommandResponse(SMB1Header header, Transaction2QueryFileInformationRequest subcommand, FileSystemShare share, SMB1ConnectionState state)
{ {
SMB1Session session = state.GetSession(header.UID); SMB1Session session = state.GetSession(header.UID);
IFileSystem fileSystem = share.FileSystem;
OpenFileObject openFile = session.GetOpenFileObject(subcommand.FID); OpenFileObject openFile = session.GetOpenFileObject(subcommand.FID);
if (openFile == null) if (openFile == null)
{ {
@ -186,15 +175,9 @@ namespace SMBLibrary.Server.SMB1
return null; return null;
} }
FileSystemEntry entry = fileSystem.GetEntry(openFile.Path);
if (entry == null)
{
header.Status = NTStatus.STATUS_NO_SUCH_FILE;
return null;
}
Transaction2QueryFileInformationResponse response = new Transaction2QueryFileInformationResponse(); Transaction2QueryFileInformationResponse response = new Transaction2QueryFileInformationResponse();
QueryInformation queryInformation; 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) if (queryStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information level: {1}, NTStatus: {2}", openFile.Path, subcommand.InformationLevel, queryStatus); 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; 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) if (status != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}' failed. Information level: {1}, NTStatus: {2}", openFile.Path, information.InformationLevel, status); 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(); Transaction2SetFileInformationResponse response = new Transaction2SetFileInformationResponse();
return response; 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();
}
}
} }
} }

View file

@ -27,8 +27,18 @@ namespace SMBLibrary.Server.SMB1
} }
TransactionTransactNamedPipeResponse response = new TransactionTransactNamedPipeResponse(); TransactionTransactNamedPipeResponse response = new TransactionTransactNamedPipeResponse();
openFile.Stream.Write(subcommand.WriteData, 0, subcommand.WriteData.Length); int numberOfBytesWritten;
response.ReadData = ByteReader.ReadAllBytes(openFile.Stream); 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; return response;
} }
} }

View file

@ -22,29 +22,21 @@ namespace SMBLibrary.Server.SMB2
{ {
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED); return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
} }
string path = openFile.Path; share.FileStore.CloseFile(openFile.Handle);
session.RemoveOpenFile(request.FileId.Persistent); session.RemoveOpenFile(request.FileId.Persistent);
CloseResponse response = new CloseResponse(); CloseResponse response = new CloseResponse();
if (request.PostQueryAttributes) if (request.PostQueryAttributes)
{ {
if (share is NamedPipeShare) FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, openFile.Path);
if (fileInfo != null)
{ {
response.FileAttributes = FileAttributes.Temporary; response.CreationTime = fileInfo.CreationTime;
} response.LastAccessTime = fileInfo.LastAccessTime;
else // FileSystemShare response.LastWriteTime = fileInfo.LastWriteTime;
{ response.ChangeTime = fileInfo.ChangeTime;
IFileSystem fileSystem = ((FileSystemShare)share).FileSystem; response.AllocationSize = fileInfo.AllocationSize;
FileSystemEntry entry = fileSystem.GetEntry(path); response.EndofFile = fileInfo.EndOfFile;
if (entry != null) response.FileAttributes = fileInfo.FileAttributes;
{
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);
}
} }
} }
return response; return response;

View file

@ -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) if (share is NamedPipeShare)
{ {
Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path); return CreateResponseForNamedPipe(persistentFileID.Value, FileStatus.FILE_OPENED);
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);
}
} }
else else
{ {
FileSystemShare fileSystemShare = (FileSystemShare)share; FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle);
CreateResponse response = CreateResponseFromFileSystemEntry(fileInfo, persistentFileID.Value, fileStatus);
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);
if (request.RequestedOplockLevel == OplockLevel.Batch) if (request.RequestedOplockLevel == OplockLevel.Batch)
{ {
response.OplockLevel = OplockLevel.Batch; response.OplockLevel = OplockLevel.Batch;
@ -91,24 +72,17 @@ namespace SMBLibrary.Server.SMB2
return response; 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(); CreateResponse response = new CreateResponse();
if (entry.IsDirectory)
{
response.FileAttributes = FileAttributes.Directory;
}
else
{
response.FileAttributes = FileAttributes.Normal;
}
response.CreateAction = (CreateAction)fileStatus; response.CreateAction = (CreateAction)fileStatus;
response.CreationTime = entry.CreationTime; response.CreationTime = fileInfo.CreationTime;
response.LastWriteTime = entry.LastWriteTime; response.LastWriteTime = fileInfo.LastWriteTime;
response.ChangeTime = entry.LastWriteTime; response.ChangeTime = fileInfo.LastWriteTime;
response.LastAccessTime = entry.LastAccessTime; response.LastAccessTime = fileInfo.LastAccessTime;
response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size); response.AllocationSize = fileInfo.AllocationSize;
response.EndofFile = (long)entry.Size; response.EndofFile = fileInfo.EndOfFile;
response.FileAttributes = fileInfo.FileAttributes;
response.FileId.Persistent = persistentFileID; response.FileId.Persistent = persistentFileID;
return response; return response;
} }

View file

@ -39,8 +39,18 @@ namespace SMBLibrary.Server.SMB2
{ {
IOCtlResponse response = new IOCtlResponse(); IOCtlResponse response = new IOCtlResponse();
response.CtlCode = request.CtlCode; response.CtlCode = request.CtlCode;
openFile.Stream.Write(request.Input, 0, request.Input.Length); int numberOfBytesWritten;
response.Output = ByteReader.ReadAllBytes(openFile.Stream); 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; return response;
} }
} }

View file

@ -34,19 +34,6 @@ namespace SMBLibrary.Server.SMB2
} }
FileSystemShare fileSystemShare = (FileSystemShare)share; 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; ulong fileID = request.FileId.Persistent;
OpenSearch openSearch = session.GetOpenSearch(fileID); OpenSearch openSearch = session.GetOpenSearch(fileID);
@ -56,8 +43,8 @@ namespace SMBLibrary.Server.SMB2
{ {
session.RemoveOpenSearch(fileID); session.RemoveOpenSearch(fileID);
} }
List<FileSystemEntry> entries; List<QueryDirectoryFileInformation> entries;
NTStatus searchStatus = NTFileSystemHelper.FindEntries(out entries, fileSystemShare.FileSystem, openFile.Path, request.FileName); NTStatus searchStatus = share.FileStore.QueryDirectory(out entries, openFile.Handle, request.FileName, request.FileInformationClass);
if (searchStatus != NTStatus.STATUS_SUCCESS) if (searchStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "Query Directory: Path: '{0}', Searched for '{1}', NTStatus: {2}", openFile.Path, request.FileName, searchStatus.ToString()); 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; int pageLength = 0;
for (int index = openSearch.EnumerationLocation; index < openSearch.Entries.Count; index++) for (int index = openSearch.EnumerationLocation; index < openSearch.Entries.Count; index++)
{ {
QueryDirectoryFileInformation fileInformation; QueryDirectoryFileInformation fileInformation = openSearch.Entries[index];
try if (fileInformation.FileInformationClass != request.FileInformationClass)
{ {
fileInformation = FromFileSystemEntry(openSearch.Entries[index], request.FileInformationClass); // We do not support changing FileInformationClass during a search (unless SMB2_REOPEN is set).
} return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
catch (UnsupportedInformationLevelException)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS);
} }
if (pageLength + fileInformation.Length <= request.OutputBufferLength) if (pageLength + fileInformation.Length <= request.OutputBufferLength)
@ -119,95 +103,5 @@ namespace SMBLibrary.Server.SMB2
response.SetFileInformationList(page); response.SetFileInformationList(page);
return response; 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();
}
}
}
} }
} }

View file

@ -25,27 +25,16 @@ namespace SMBLibrary.Server.SMB2
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED); return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
} }
FileInformation fileInformation; if (share is FileSystemShare)
NTStatus queryStatus;
if (share is NamedPipeShare)
{
queryStatus = NTFileSystemHelper.GetNamedPipeInformation(out fileInformation, request.FileInformationClass);
}
else // FileSystemShare
{ {
if (!((FileSystemShare)share).HasReadAccess(session.UserName, openFile.Path, state.ClientEndPoint)) if (!((FileSystemShare)share).HasReadAccess(session.UserName, openFile.Path, state.ClientEndPoint))
{ {
return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED); 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) if (queryStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, request.FileInformationClass, queryStatus); 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); return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
} }
IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
FileSystemInformation fileSystemInformation; 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) if (queryStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information class: {0}, NTStatus: {1}", request.FileSystemInformationClass, queryStatus); state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information class: {0}, NTStatus: {1}", request.FileSystemInformationClass, queryStatus);

View file

@ -24,7 +24,7 @@ namespace SMBLibrary.Server.SMB2
} }
byte[] data; 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) if (readStatus != NTStatus.STATUS_SUCCESS)
{ {
return new ErrorResponse(request.CommandName, readStatus); return new ErrorResponse(request.CommandName, readStatus);
@ -44,7 +44,7 @@ namespace SMBLibrary.Server.SMB2
} }
int numberOfBytesWritten; 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) if (writeStatus != NTStatus.STATUS_SUCCESS)
{ {
return new ErrorResponse(request.CommandName, writeStatus); return new ErrorResponse(request.CommandName, writeStatus);

View file

@ -27,47 +27,37 @@ namespace SMBLibrary.Server.SMB2
if (share is FileSystemShare) if (share is FileSystemShare)
{ {
IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
if (!((FileSystemShare)share).HasWriteAccess(session.UserName, openFile.Path, state.ClientEndPoint)) if (!((FileSystemShare)share).HasWriteAccess(session.UserName, openFile.Path, state.ClientEndPoint))
{ {
return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED); 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); return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
} }

View file

@ -199,7 +199,7 @@ namespace SMBLibrary.Server
return new ErrorResponse(command.CommandName); return new ErrorResponse(command.CommandName);
} }
QueryInformationRequest request = (QueryInformationRequest)command; QueryInformationRequest request = (QueryInformationRequest)command;
return FileSystemResponseHelper.GetQueryInformationResponse(header, request, (FileSystemShare)share); return FileSystemResponseHelper.GetQueryInformationResponse(header, request, (FileSystemShare)share, state);
} }
else if (command is SetInformationRequest) else if (command is SetInformationRequest)
{ {
@ -229,7 +229,7 @@ namespace SMBLibrary.Server
return new ErrorResponse(command.CommandName); return new ErrorResponse(command.CommandName);
} }
CheckDirectoryRequest request = (CheckDirectoryRequest)command; CheckDirectoryRequest request = (CheckDirectoryRequest)command;
return FileSystemResponseHelper.GetCheckDirectoryResponse(header, request, (FileSystemShare)share); return FileSystemResponseHelper.GetCheckDirectoryResponse(header, request, (FileSystemShare)share, state);
} }
else if (command is WriteRawRequest) else if (command is WriteRawRequest)
{ {

View file

@ -177,7 +177,11 @@ namespace SMBLibrary.Server
{ {
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED); 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(); return new FlushResponse();
} }
else if (command is CloseRequest) else if (command is CloseRequest)

View file

@ -33,14 +33,20 @@ namespace SMBLibrary.Server
public class FileSystemShare : ISMBShare public class FileSystemShare : ISMBShare
{ {
private string m_name; private string m_name;
public IFileSystem m_fileSystem; private INTFileStore m_fileSystem;
public event EventHandler<AccessRequestArgs> OnAccessRequest; public event EventHandler<AccessRequestArgs> OnAccessRequest;
public FileSystemShare(string shareName, INTFileStore fileSystem)
{
m_name = shareName;
m_fileSystem = fileSystem;
}
public FileSystemShare(string shareName, IFileSystem fileSystem) public FileSystemShare(string shareName, IFileSystem fileSystem)
{ {
m_name = shareName; m_name = shareName;
m_fileSystem = fileSystem; m_fileSystem = new NTFileSystemAdapter(fileSystem);
} }
public bool HasReadAccess(string userName, string path, IPEndPoint clientEndPoint) public bool HasReadAccess(string userName, string path, IPEndPoint clientEndPoint)
@ -74,7 +80,7 @@ namespace SMBLibrary.Server
} }
} }
public IFileSystem FileSystem public INTFileStore FileStore
{ {
get get
{ {

View file

@ -14,5 +14,10 @@ namespace SMBLibrary.Server
{ {
get; get;
} }
INTFileStore FileStore
{
get;
}
} }
} }

View file

@ -12,45 +12,19 @@ using SMBLibrary.Services;
namespace SMBLibrary.Server namespace SMBLibrary.Server
{ {
public class NamedPipeShare : List<RemoteService>, ISMBShare public class NamedPipeShare : ISMBShare
{ {
// A pipe share, as defined by the SMB Protocol, MUST always have the name "IPC$". // A pipe share, as defined by the SMB Protocol, MUST always have the name "IPC$".
public const string NamedPipeShareName = "IPC$"; public const string NamedPipeShareName = "IPC$";
private NamedPipeStore m_store;
public NamedPipeShare(List<string> shareList) public NamedPipeShare(List<string> shareList)
{ {
this.Add(new ServerService(Environment.MachineName, shareList)); List<RemoteService> services = new List<RemoteService>();
this.Add(new WorkstationService(Environment.MachineName, Environment.MachineName)); services.Add(new ServerService(Environment.MachineName, shareList));
} services.Add(new WorkstationService(Environment.MachineName, Environment.MachineName));
m_store = new NamedPipeStore(services);
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;
} }
public string Name public string Name
@ -60,5 +34,13 @@ namespace SMBLibrary.Server
return NamedPipeShareName; return NamedPipeShareName;
} }
} }
public INTFileStore FileStore
{
get
{
return m_store;
}
}
} }
} }