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; } }