NTFileSystemHelper: CreateFile now returns FileStatus

This commit is contained in:
Tal Aloni 2017-02-11 01:17:46 +02:00
parent 1b31200dc3
commit 756fbb96fa
5 changed files with 109 additions and 73 deletions

View file

@ -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,
}
}

View file

@ -89,6 +89,7 @@
<Compile Include="NTFileStore\Enums\FileSystemInformation\SectorSizeInformationFlags.cs" />
<Compile Include="NTFileStore\Enums\NtCreateFile\CreateDisposition.cs" />
<Compile Include="NTFileStore\Enums\NtCreateFile\CreateOptions.cs" />
<Compile Include="NTFileStore\Enums\NtCreateFile\FileStatus.cs" />
<Compile Include="NTFileStore\Enums\NtCreateFile\ShareAccess.cs" />
<Compile Include="NTFileStore\NTFileStoreHelper.cs" />
<Compile Include="NTFileStore\Structures\ACE\AccessAllowedACE.cs" />

View file

@ -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

View file

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

View file

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