diff --git a/SMBLibrary/NTFileStore/Enums/NtCreateFile/FileStatus.cs b/SMBLibrary/NTFileStore/Enums/NtCreateFile/FileStatus.cs
new file mode 100644
index 0000000..0a1324e
--- /dev/null
+++ b/SMBLibrary/NTFileStore/Enums/NtCreateFile/FileStatus.cs
@@ -0,0 +1,13 @@
+
+namespace SMBLibrary
+{
+ public enum FileStatus : uint
+ {
+ FILE_SUPERSEDED = 0x00000000,
+ FILE_OPENED = 0x00000001,
+ FILE_CREATED = 0x00000002,
+ FILE_OVERWRITTEN = 0x00000003,
+ FILE_EXISTS = 0x00000004,
+ FILE_DOES_NOT_EXIST = 0x00000005,
+ }
+}
diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj
index 95fc7f3..b080c73 100644
--- a/SMBLibrary/SMBLibrary.csproj
+++ b/SMBLibrary/SMBLibrary.csproj
@@ -89,6 +89,7 @@
+
diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs b/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs
index 7995d75..833dd06 100644
--- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs
+++ b/SMBLibrary/Server/Helpers/NTFileSystemHelper.cs
@@ -20,8 +20,10 @@ namespace SMBLibrary.Server
public const int BytesPerSector = 512;
public const int ClusterSize = 4096;
- public static NTStatus CreateFile(out FileSystemEntry entry, IFileSystem fileSystem, string path, AccessMask desiredAccess, CreateDisposition createDisposition, CreateOptions createOptions, ConnectionState state)
+ 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)
{
+ fileStatus = FileStatus.FILE_DOES_NOT_EXIST;
+ stream = null;
FileAccess createAccess = NTFileStoreHelper.ToCreateFileAccess(desiredAccess, createDisposition);
bool requestedWriteAccess = (createAccess & FileAccess.Write) > 0;
@@ -64,6 +66,7 @@ namespace SMBLibrary.Server
return NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
}
+ fileStatus = FileStatus.FILE_EXISTS;
if (entry.IsDirectory && forceFile)
{
return NTStatus.STATUS_FILE_IS_A_DIRECTORY;
@@ -81,6 +84,7 @@ namespace SMBLibrary.Server
{
// File already exists, fail the request
state.LogToServer(Severity.Debug, "CreateFile: File '{0}' already exist", path);
+ fileStatus = FileStatus.FILE_EXISTS;
return NTStatus.STATUS_OBJECT_NAME_COLLISION;
}
@@ -108,6 +112,7 @@ namespace SMBLibrary.Server
state.LogToServer(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status);
return status;
}
+ fileStatus = FileStatus.FILE_CREATED;
}
else if (createDisposition == CreateDisposition.FILE_OPEN_IF ||
createDisposition == CreateDisposition.FILE_OVERWRITE ||
@@ -145,9 +150,11 @@ namespace SMBLibrary.Server
state.LogToServer(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status);
return status;
}
+ fileStatus = FileStatus.FILE_CREATED;
}
else
{
+ fileStatus = FileStatus.FILE_EXISTS;
if (!requestedWriteAccess)
{
return NTStatus.STATUS_ACCESS_DENIED;
@@ -168,6 +175,7 @@ namespace SMBLibrary.Server
state.LogToServer(Severity.Debug, "CreateFile: Error truncating '{0}'. {1}.", path, status);
return status;
}
+ fileStatus = FileStatus.FILE_OVERWRITTEN;
}
else if (createDisposition == CreateDisposition.FILE_SUPERSEDE)
{
@@ -202,6 +210,7 @@ namespace SMBLibrary.Server
state.LogToServer(Severity.Debug, "CreateFile: Error creating '{0}'. {1}.", path, status);
return status;
}
+ fileStatus = FileStatus.FILE_SUPERSEDED;
}
}
}
@@ -210,12 +219,40 @@ namespace SMBLibrary.Server
return NTStatus.STATUS_INVALID_PARAMETER;
}
+ FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(desiredAccess.File);
+ bool deleteOnClose = false;
+ if (fileAccess == (FileAccess)0 || entry.IsDirectory)
+ {
+ stream = null;
+ }
+ else
+ {
+ deleteOnClose = (createOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0;
+ NTStatus openStatus = OpenFileStream(out stream, fileSystem, path, fileAccess, shareAccess, createOptions, state);
+ if (openStatus != NTStatus.STATUS_SUCCESS)
+ {
+ return openStatus;
+ }
+ }
+
+ if (fileStatus != FileStatus.FILE_CREATED &&
+ fileStatus != FileStatus.FILE_OVERWRITTEN &&
+ fileStatus != FileStatus.FILE_SUPERSEDED)
+ {
+ fileStatus = FileStatus.FILE_OPENED;
+ }
return NTStatus.STATUS_SUCCESS;
}
- public static NTStatus OpenFile(out Stream stream, IFileSystem fileSystem, string path, FileAccess fileAccess, ShareAccess shareAccess, bool buffered, ConnectionState state)
+ public static NTStatus OpenFileStream(out Stream stream, IFileSystem fileSystem, string path, FileAccess fileAccess, ShareAccess shareAccess, CreateOptions openOptions, ConnectionState state)
{
stream = null;
+ // When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point.
+ // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception
+ // of a file copy operation (where the caller will attempt to simply copy the reparse point).
+ bool openReparsePoint = (openOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0;
+ bool disableBuffering = (openOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
+ bool buffered = (openOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
FileShare fileShare = NTFileStoreHelper.ToFileShare(shareAccess);
state.LogToServer(Severity.Verbose, "OpenFile: Opening '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShare, buffered);
try
diff --git a/SMBLibrary/Server/SMB1/NTCreateHelper.cs b/SMBLibrary/Server/SMB1/NTCreateHelper.cs
index e629ad8..bfeb61f 100644
--- a/SMBLibrary/Server/SMB1/NTCreateHelper.cs
+++ b/SMBLibrary/Server/SMB1/NTCreateHelper.cs
@@ -44,11 +44,11 @@ namespace SMBLibrary.Server.SMB1
}
if (isExtended)
{
- return CreateResponseExtendedForNamedPipe(fileID.Value);
+ return CreateResponseExtendedForNamedPipe(fileID.Value, FileStatus.FILE_OPENED);
}
else
{
- return CreateResponseForNamedPipe(fileID.Value);
+ return CreateResponseForNamedPipe(fileID.Value, FileStatus.FILE_OPENED);
}
}
@@ -60,7 +60,9 @@ namespace SMBLibrary.Server.SMB1
FileSystemShare fileSystemShare = (FileSystemShare)share;
FileSystemEntry entry;
- NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, fileSystemShare.FileSystem, path, request.DesiredAccess, request.CreateDisposition, request.CreateOptions, state);
+ 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;
@@ -69,39 +71,20 @@ namespace SMBLibrary.Server.SMB1
FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(request.DesiredAccess);
- Stream stream;
- bool deleteOnClose = false;
- if (fileAccess == (FileAccess)0 || entry.IsDirectory)
- {
- stream = null;
- }
- else
- {
- IFileSystem fileSystem = fileSystemShare.FileSystem;
- // When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point.
- // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception
- // of a file copy operation (where the caller will attempt to simply copy the reparse point).
- deleteOnClose = (request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0;
- bool openReparsePoint = (request.CreateOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0;
- bool disableBuffering = (request.CreateOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
- bool buffered = (request.CreateOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
- NTStatus openStatus = NTFileSystemHelper.OpenFile(out stream, fileSystem, path, fileAccess, request.ShareAccess, buffered, state);
- if (openStatus != NTStatus.STATUS_SUCCESS)
- {
- header.Status = openStatus;
- return new ErrorResponse(request.CommandName);
- }
- }
-
+ 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)
{
- NTCreateAndXResponseExtended response = CreateResponseExtendedFromFileSystemEntry(entry, fileID.Value);
+ NTCreateAndXResponseExtended response = CreateResponseExtendedFromFileSystemEntry(entry, fileID.Value, fileStatus);
if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0)
{
response.OpLockLevel = OpLockLevel.BatchOpLockGranted;
@@ -110,7 +93,7 @@ namespace SMBLibrary.Server.SMB1
}
else
{
- NTCreateAndXResponse response = CreateResponseFromFileSystemEntry(entry, fileID.Value);
+ NTCreateAndXResponse response = CreateResponseFromFileSystemEntry(entry, fileID.Value, fileStatus);
if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0)
{
response.OpLockLevel = OpLockLevel.BatchOpLockGranted;
@@ -120,11 +103,11 @@ namespace SMBLibrary.Server.SMB1
}
}
- private static NTCreateAndXResponse CreateResponseForNamedPipe(ushort fileID)
+ private static NTCreateAndXResponse CreateResponseForNamedPipe(ushort fileID, FileStatus fileStatus)
{
NTCreateAndXResponse response = new NTCreateAndXResponse();
response.FID = fileID;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
+ response.CreateDisposition = ToCreateDisposition(fileStatus);
response.ExtFileAttributes = ExtendedFileAttributes.Normal;
response.ResourceType = ResourceType.FileTypeMessageModePipe;
response.NMPipeStatus.ICount = 255;
@@ -133,11 +116,11 @@ namespace SMBLibrary.Server.SMB1
return response;
}
- private static NTCreateAndXResponseExtended CreateResponseExtendedForNamedPipe(ushort fileID)
+ private static NTCreateAndXResponseExtended CreateResponseExtendedForNamedPipe(ushort fileID, FileStatus fileStatus)
{
NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended();
response.FID = fileID;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
+ response.CreateDisposition = ToCreateDisposition(fileStatus);
response.ExtFileAttributes = ExtendedFileAttributes.Normal;
response.ResourceType = ResourceType.FileTypeMessageModePipe;
NamedPipeStatus status = new NamedPipeStatus();
@@ -157,7 +140,7 @@ namespace SMBLibrary.Server.SMB1
return response;
}
- private static NTCreateAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID)
+ private static NTCreateAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID, FileStatus fileStatus)
{
NTCreateAndXResponse response = new NTCreateAndXResponse();
if (entry.IsDirectory)
@@ -170,7 +153,7 @@ namespace SMBLibrary.Server.SMB1
response.ExtFileAttributes = ExtendedFileAttributes.Normal;
}
response.FID = fileID;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
+ response.CreateDisposition = ToCreateDisposition(fileStatus);
response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size);
response.EndOfFile = (long)entry.Size;
response.CreateTime = entry.CreationTime;
@@ -181,7 +164,7 @@ namespace SMBLibrary.Server.SMB1
return response;
}
- private static NTCreateAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID)
+ private static NTCreateAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID, FileStatus fileStatus)
{
NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended();
if (entry.IsDirectory)
@@ -194,11 +177,11 @@ namespace SMBLibrary.Server.SMB1
response.ExtFileAttributes = ExtendedFileAttributes.Normal;
}
response.FID = fileID;
+ response.CreateDisposition = ToCreateDisposition(fileStatus);
response.CreateTime = entry.CreationTime;
response.LastAccessTime = entry.LastAccessTime;
response.LastWriteTime = entry.LastWriteTime;
response.LastChangeTime = entry.LastWriteTime;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size);
response.EndOfFile = (long)entry.Size;
response.ResourceType = ResourceType.FileTypeDisk;
@@ -214,5 +197,25 @@ namespace SMBLibrary.Server.SMB1
FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE;
return response;
}
+
+ private static CreateDisposition ToCreateDisposition(FileStatus fileStatus)
+ {
+ if (fileStatus == FileStatus.FILE_SUPERSEDED)
+ {
+ return CreateDisposition.FILE_SUPERSEDE;
+ }
+ else if (fileStatus == FileStatus.FILE_CREATED)
+ {
+ return CreateDisposition.FILE_CREATE;
+ }
+ else if (fileStatus == FileStatus.FILE_OVERWRITTEN)
+ {
+ return CreateDisposition.FILE_OVERWRITE;
+ }
+ else
+ {
+ return CreateDisposition.FILE_OPEN;
+ }
+ }
}
}
diff --git a/SMBLibrary/Server/SMB2/CreateHelper.cs b/SMBLibrary/Server/SMB2/CreateHelper.cs
index 0416d6c..ade1115 100644
--- a/SMBLibrary/Server/SMB2/CreateHelper.cs
+++ b/SMBLibrary/Server/SMB2/CreateHelper.cs
@@ -42,7 +42,7 @@ namespace SMBLibrary.Server.SMB2
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES);
}
- return CreateResponseForNamedPipe(persistentFileID.Value);
+ return CreateResponseForNamedPipe(persistentFileID.Value, FileStatus.FILE_OPENED);
}
else
{
@@ -54,44 +54,26 @@ namespace SMBLibrary.Server.SMB2
FileSystemShare fileSystemShare = (FileSystemShare)share;
FileSystemEntry entry;
- NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, fileSystemShare.FileSystem, path, request.DesiredAccess, request.CreateDisposition, request.CreateOptions, state);
+ 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);
}
- FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(request.DesiredAccess.File);
-
- Stream stream;
- bool deleteOnClose = false;
- if (fileAccess == (FileAccess)0 || entry.IsDirectory)
- {
- stream = null;
- }
- else
- {
- IFileSystem fileSystem = fileSystemShare.FileSystem;
- // When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point.
- // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception
- // of a file copy operation (where the caller will attempt to simply copy the reparse point).
- deleteOnClose = (request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0;
- bool openReparsePoint = (request.CreateOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0;
- bool disableBuffering = (request.CreateOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
- bool buffered = (request.CreateOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
- NTStatus openStatus = NTFileSystemHelper.OpenFile(out stream, fileSystem, path, fileAccess, request.ShareAccess, buffered, state);
- if (openStatus != NTStatus.STATUS_SUCCESS)
- {
- return new ErrorResponse(request.CommandName, openStatus);
- }
- }
-
+ 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);
+ CreateResponse response = CreateResponseFromFileSystemEntry(entry, persistentFileID.Value, fileStatus);
if (request.RequestedOplockLevel == OplockLevel.Batch)
{
response.OplockLevel = OplockLevel.Batch;
@@ -100,16 +82,16 @@ namespace SMBLibrary.Server.SMB2
}
}
- private static CreateResponse CreateResponseForNamedPipe(ulong persistentFileID)
+ private static CreateResponse CreateResponseForNamedPipe(ulong persistentFileID, FileStatus fileStatus)
{
CreateResponse response = new CreateResponse();
- response.FileId.Persistent = persistentFileID;
- response.CreateAction = CreateAction.FILE_OPENED;
+ response.CreateAction = (CreateAction)fileStatus;
response.FileAttributes = FileAttributes.Normal;
+ response.FileId.Persistent = persistentFileID;
return response;
}
- private static CreateResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ulong persistentFileID)
+ private static CreateResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ulong persistentFileID, FileStatus fileStatus)
{
CreateResponse response = new CreateResponse();
if (entry.IsDirectory)
@@ -120,14 +102,14 @@ namespace SMBLibrary.Server.SMB2
{
response.FileAttributes = FileAttributes.Normal;
}
- response.FileId.Persistent = persistentFileID;
- response.CreateAction = CreateAction.FILE_OPENED;
+ response.CreateAction = (CreateAction)fileStatus;
response.CreationTime = entry.CreationTime;
response.LastWriteTime = entry.LastWriteTime;
response.ChangeTime = entry.LastWriteTime;
response.LastAccessTime = entry.LastAccessTime;
response.AllocationSize = (long)NTFileSystemHelper.GetAllocationSize(entry.Size);
response.EndofFile = (long)entry.Size;
+ response.FileId.Persistent = persistentFileID;
return response;
}
}