sys: implement named stream open/close and related delete/share access issues

This commit is contained in:
Bill Zissimopoulos 2016-09-27 14:53:59 -07:00
parent 2ba46fdb71
commit 495fc7a5dc
7 changed files with 274 additions and 15 deletions

View File

@ -167,12 +167,21 @@ static VOID FspFsvolCleanupRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Co
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
HANDLE MainStreamHandle;
ASSERT(FileNode == FileDesc->FileNode);
FspFileNodeReleaseOwner(FileNode, Pgio, Request);
FspFileNodeCleanupComplete(FileNode, FileObject);
MainStreamHandle = FileDesc->MainStreamHandle;
FileDesc->MainStreamHandle = 0;
FspFileNodeReleaseOwner(FileNode, Main, Request);
FspMainStreamClose(MainStreamHandle, 0);
}
NTSTATUS FspCleanup(

View File

@ -77,7 +77,7 @@ static NTSTATUS FspFsvolClose(
FspFileNodeClose(FileNode, FileObject);
/* delete the FileDesc and deref the FileNode; order is important (FileDesc has FileNode ref) */
FspFileDescDelete(FileDesc);
FspFileDescDelete(FileDesc); /* this will also close the MainStreamObject if any */
FspFileNodeDereference(FileNode);
/*

View File

@ -134,7 +134,6 @@ static NTSTATUS FspFsvolCreateNoLock(
PFILE_OBJECT FileObject = IrpSp->FileObject;
PFILE_OBJECT RelatedFileObject = FileObject->RelatedFileObject;
UNICODE_STRING FileName = FileObject->FileName;
UNICODE_STRING MainStreamName = { 0 }, StreamPart = { 0 };
/* open the volume object? */
if ((0 == RelatedFileObject || !FspFileNodeIsValid(RelatedFileObject->FsContext)) &&
@ -148,6 +147,7 @@ static NTSTATUS FspFsvolCreateNoLock(
return STATUS_SUCCESS;
}
UNICODE_STRING MainStreamName = { 0 }, StreamPart = { 0 };
PACCESS_STATE AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
ULONG CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0xff;
ULONG CreateOptions = IrpSp->Parameters.Create.Options;
@ -163,6 +163,10 @@ static NTSTATUS FspFsvolCreateNoLock(
ULONG Flags = IrpSp->Flags;
KPROCESSOR_MODE RequestorMode =
FlagOn(Flags, SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode;
BOOLEAN CaseSensitiveRequested =
BooleanFlagOn(Flags, SL_CASE_SENSITIVE);
BOOLEAN CaseSensitive =
CaseSensitiveRequested || FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch;
BOOLEAN HasTraversePrivilege =
BooleanFlagOn(AccessState->Flags, TOKEN_HAS_TRAVERSE_PRIVILEGE);
FSP_FILE_NODE *FileNode, *RelatedFileNode;
@ -347,6 +351,52 @@ static NTSTATUS FspFsvolCreateNoLock(
return Result;
}
/* if we have a non-empty stream part, open the main stream */
if (0 != StreamPart.Buffer)
{
Result = FspMainStreamOpen(FsvolDeviceObject,
&MainStreamName, CaseSensitive,
CreateDisposition,
&FileDesc->MainStreamHandle,
&FileDesc->MainStreamObject);
if (!NT_SUCCESS(Result))
goto main_stream_exit;
/* check that the main stream is one we recognize */
if (!FspFileNodeIsValid(FileDesc->MainStreamObject->FsContext))
{
Result = STATUS_OBJECT_NAME_NOT_FOUND;
goto main_stream_exit;
}
/* named streams can never be directories (even when attached to directories) */
if (FlagOn(CreateOptions, FILE_DIRECTORY_FILE))
{
Result = STATUS_NOT_A_DIRECTORY;
goto main_stream_exit;
}
/* cannot open target directory of a named stream */
if (FlagOn(Flags, SL_OPEN_TARGET_DIRECTORY))
{
Result = STATUS_OBJECT_NAME_INVALID;
goto main_stream_exit;
}
/* remember the the main stream node */
FileNode->MainStreamFileNode = FileDesc->MainStreamObject->FsContext;
Result = STATUS_SUCCESS;
main_stream_exit:
if (!NT_SUCCESS(Result))
{
FspFileDescDelete(FileDesc);
FspFileNodeDereference(FileNode);
return Result;
}
}
/* create the user-mode file system request */
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, SecurityDescriptorSize,
FspFsvolCreateRequestFini, &Request);
@ -369,9 +419,7 @@ static NTSTATUS FspFsvolCreateNoLock(
* delete the Request and any associated resources.
*/
FileDesc->FileNode = FileNode;
FileDesc->CaseSensitive =
0 != FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch ||
BooleanFlagOn(Flags, SL_CASE_SENSITIVE);
FileDesc->CaseSensitive = CaseSensitive;
FileDesc->HasTraversePrivilege = HasTraversePrivilege;
FspFsvolDeviceFileRenameSetOwner(FsvolDeviceObject, Request);
FspIopRequestContext(Request, RequestDeviceObject) = FsvolDeviceObject;
@ -393,7 +441,7 @@ static NTSTATUS FspFsvolCreateNoLock(
Request->Req.Create.UserMode = UserMode == RequestorMode;
Request->Req.Create.HasTraversePrivilege = HasTraversePrivilege;
Request->Req.Create.OpenTargetDirectory = BooleanFlagOn(Flags, SL_OPEN_TARGET_DIRECTORY);
Request->Req.Create.CaseSensitive = BooleanFlagOn(Flags, SL_CASE_SENSITIVE);
Request->Req.Create.CaseSensitive = CaseSensitiveRequested;
/* copy the security descriptor (if any) into the request */
if (0 != SecurityDescriptorSize)

View File

@ -431,6 +431,15 @@ VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE
NTSTATUS FspCreateGuid(GUID *Guid);
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject);
NTSTATUS FspMainStreamOpen(
PDEVICE_OBJECT DeviceObject,
PUNICODE_STRING MainStreamName, BOOLEAN CaseSensitive,
ULONG Disposition,
PHANDLE PMainStreamHandle,
PFILE_OBJECT *PMainStreamObject);
NTSTATUS FspMainStreamClose(
HANDLE MainStreamHandle,
PFILE_OBJECT MainStreamObject);
NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length);
NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
@ -896,7 +905,7 @@ typedef struct
UINT64 DirInfo;
UINT64 StreamInfo;
} FSP_FILE_NODE_NONPAGED;
typedef struct
typedef struct FSP_FILE_NODE
{
FSRTL_ADVANCED_FCB_HEADER Header;
FSP_FILE_NODE_NONPAGED *NonPaged;
@ -907,6 +916,8 @@ typedef struct
LONG OpenCount; /* ContextTable ref count */
LONG HandleCount; /* HANDLE count (CREATE/CLEANUP) */
SHARE_ACCESS ShareAccess;
ULONG MainStreamDenyDeleteCount; /* number of times main stream is denying delete */
ULONG StreamDenyDeleteCount; /* number of open streams that are denying delete */
FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT ContextByNameElementStorage;
/* locked under FSP_FSVOL_DEVICE_EXTENSION::FileRenameResource or Header.Resource */
UNICODE_STRING FileName;
@ -941,6 +952,7 @@ typedef struct
UINT64 IndexNumber;
BOOLEAN IsDirectory;
BOOLEAN IsRootDirectory;
struct FSP_FILE_NODE *MainStreamFileNode; /* this becomes invalid after our last desc close */
WCHAR FileNameBuf[];
} FSP_FILE_NODE;
typedef struct
@ -955,6 +967,9 @@ typedef struct
UINT64 DirectoryOffset;
UINT64 DirInfo;
ULONG DirInfoCacheHint;
/* stream support */
HANDLE MainStreamHandle;
PFILE_OBJECT MainStreamObject;
} FSP_FILE_DESC;
NTSTATUS FspFileNodeCopyList(PDEVICE_OBJECT DeviceObject,
FSP_FILE_NODE ***PFileNodes, PULONG PFileNodeCount);

View File

@ -420,12 +420,37 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
PAGED_CODE();
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
FSP_FILE_NODE *OpenedFileNode;
FSP_FILE_NODE *OpenedFileNode = 0;
BOOLEAN Inserted, DeletePending;
NTSTATUS Result;
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
/*
* If this is a named stream we must also check with our main stream.
* Note that FileNode->MainStreamFileNode and OpenedFileNode->MainStreamFileNode
* will always be the same.
*/
if (0 != FileNode->MainStreamFileNode)
{
DeletePending = 0 != FileNode->MainStreamFileNode->DeletePending;
MemoryBarrier();
if (DeletePending)
{
Result = STATUS_DELETE_PENDING;
goto exit;
}
if (0 < FileNode->MainStreamFileNode->MainStreamDenyDeleteCount)
{
if (FlagOn(GrantedAccess, DELETE))
{
Result = STATUS_SHARING_VIOLATION;
goto exit;
}
}
}
OpenedFileNode = FspFsvolDeviceInsertContextByName(FsvolDeviceObject,
&FileNode->FileName, FileNode, &FileNode->ContextByNameElementStorage, &Inserted);
ASSERT(0 != OpenedFileNode);
@ -449,6 +474,7 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
* opening a prior FileNode that we found in the table.
*/
ASSERT(OpenedFileNode != FileNode);
ASSERT(OpenedFileNode->MainStreamFileNode == FileNode->MainStreamFileNode);
DeletePending = 0 != OpenedFileNode->DeletePending;
MemoryBarrier();
@ -458,6 +484,19 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
goto exit;
}
/* if this is a main stream check whether there is a named stream that denies delete */
if (0 < OpenedFileNode->StreamDenyDeleteCount)
{
/* we must be the main stream! */
ASSERT(0 == OpenedFileNode->MainStreamFileNode);
if (FlagOn(GrantedAccess, DELETE))
{
Result = STATUS_SHARING_VIOLATION;
goto exit;
}
}
/*
* FastFat says to do the following on Vista and above.
*
@ -478,15 +517,33 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
/* share access check */
Result = IoCheckShareAccess(GrantedAccess, ShareAccess, FileObject,
&OpenedFileNode->ShareAccess, TRUE);
exit:
if (!NT_SUCCESS(Result))
{
if (0 != PResult)
*PResult = Result;
goto exit;
}
OpenedFileNode = 0;
}
/*
* No more failures allowed at this point!
* This is because we have potentially inserted a new FileNode into the Context table.
* We also updated OpenedFileNode->ShareAccess in IoSetShareAccess/IoCheckShareAccess.
*/
if (FileObject->DeleteAccess && !FileObject->SharedDelete)
{
if (0 == OpenedFileNode->MainStreamFileNode)
OpenedFileNode->MainStreamDenyDeleteCount++;
else
OpenedFileNode->MainStreamFileNode->StreamDenyDeleteCount++;
}
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
{
if (0 != PResult)
*PResult = Result;
OpenedFileNode = 0;
}
if (0 != OpenedFileNode)
@ -556,6 +613,14 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
if (FileObject->DeleteAccess && !FileObject->SharedDelete)
{
if (0 == FileNode->MainStreamFileNode)
FileNode->MainStreamDenyDeleteCount--;
else
FileNode->MainStreamFileNode->StreamDenyDeleteCount--;
}
IoRemoveShareAccess(FileObject, &FileNode->ShareAccess);
if (0 == --FileNode->HandleCount)
@ -1088,6 +1153,8 @@ VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc)
{
PAGED_CODE();
FspMainStreamClose(FileDesc->MainStreamHandle, FileDesc->MainStreamObject);
if (0 != FileDesc->DirectoryPattern.Buffer &&
FspFileDescDirectoryPatternMatchAll != FileDesc->DirectoryPattern.Buffer)
{

View File

@ -449,6 +449,9 @@ static NTSTATUS FspFsvolQueryStreamInformation(
{
PAGED_CODE();
if (!FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.NamedStreams)
return STATUS_INVALID_PARAMETER;
NTSTATUS Result;
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
@ -499,6 +502,10 @@ static NTSTATUS FspFsvolQueryStreamInformationSuccess(
{
PAGED_CODE();
if (!FspFsvolDeviceExtension(IoGetCurrentIrpStackLocation(Irp)->DeviceObject)->
VolumeParams.NamedStreams)
return STATUS_INVALID_PARAMETER;
NTSTATUS Result;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;

View File

@ -23,6 +23,15 @@ VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE
NTSTATUS FspCreateGuid(GUID *Guid);
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject);
NTSTATUS FspMainStreamOpen(
PDEVICE_OBJECT DeviceObject,
PUNICODE_STRING MainStreamName, BOOLEAN CaseSensitive,
ULONG Disposition,
PHANDLE PMainStreamHandle,
PFILE_OBJECT *PMainStreamObject);
NTSTATUS FspMainStreamClose(
HANDLE MainStreamHandle,
PFILE_OBJECT MainStreamObject);
NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length);
static NTSTATUS FspSendSetInformationIrpCompletion(
@ -93,6 +102,8 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
#pragma alloc_text(PAGE, FspUnicodePathSuffix)
#pragma alloc_text(PAGE, FspCreateGuid)
#pragma alloc_text(PAGE, FspGetDeviceObjectPointer)
#pragma alloc_text(PAGE, FspMainStreamOpen)
#pragma alloc_text(PAGE, FspMainStreamClose)
#pragma alloc_text(PAGE, FspSendSetInformationIrp)
#pragma alloc_text(PAGE, FspBufferUserBuffer)
#pragma alloc_text(PAGE, FspLockUserBuffer)
@ -373,6 +384,108 @@ NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK Desir
return Result;
}
NTSTATUS FspMainStreamOpen(
PDEVICE_OBJECT DeviceObject,
PUNICODE_STRING MainStreamName, BOOLEAN CaseSensitive,
ULONG Disposition,
PHANDLE PMainStreamHandle,
PFILE_OBJECT *PMainStreamObject)
{
PAGED_CODE();
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatus;
HANDLE MainStreamHandle;
PFILE_OBJECT MainStreamObject;
/* assert that the supplied name is actually a main stream name */
ASSERT(FspUnicodePathIsValid(MainStreamName, 0));
*PMainStreamHandle = 0;
*PMainStreamObject = 0;
switch (Disposition)
{
case FILE_CREATE:
case FILE_OPEN_IF:
case FILE_OVERWRITE_IF:
Disposition = FILE_OPEN_IF;
break;
case FILE_OPEN:
case FILE_OVERWRITE:
case FILE_SUPERSEDE:
Disposition = FILE_OPEN;
break;
default:
return STATUS_INVALID_PARAMETER;
}
InitializeObjectAttributes(
&ObjectAttributes,
MainStreamName,
OBJ_KERNEL_HANDLE | OBJ_FORCE_ACCESS_CHECK | (CaseSensitive ? 0 : OBJ_CASE_INSENSITIVE),
0/*RootDirectory*/,
0/*SecurityDescriptor*/);
IoStatus.Status = IoCreateFileSpecifyDeviceObjectHint(
&MainStreamHandle,
FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatus,
0/*AllocationSize*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
Disposition,
FILE_OPEN_REPARSE_POINT,
0/*EaBuffer*/,
0/*EaLength*/,
CreateFileTypeNone,
0/*InternalParameters*/,
IO_FORCE_ACCESS_CHECK,
DeviceObject);
if (!NT_SUCCESS(IoStatus.Status))
return IoStatus.Status;
IoStatus.Status = ObReferenceObjectByHandle(
MainStreamHandle,
0/*DesiredAccess*/,
*IoFileObjectType,
KernelMode,
&MainStreamObject,
0/*HandleInformation*/);
if (!NT_SUCCESS(IoStatus.Status))
{
ObCloseHandle(MainStreamHandle, KernelMode);
return IoStatus.Status;
}
*PMainStreamHandle = MainStreamHandle;
*PMainStreamObject = MainStreamObject;
return STATUS_SUCCESS;
}
NTSTATUS FspMainStreamClose(
HANDLE MainStreamHandle,
PFILE_OBJECT MainStreamObject)
{
PAGED_CODE();
NTSTATUS Result = STATUS_SUCCESS;
if (0 != MainStreamObject)
ObDereferenceObject(MainStreamObject);
if (0 != MainStreamHandle)
{
Result = ObCloseHandle(MainStreamHandle, KernelMode);
if (!NT_SUCCESS(Result))
DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result));
}
return Result;
}
typedef struct
{
IO_STATUS_BLOCK IoStatus;