sys: IRP_MJ_CREATE: refactoring to support Overwrite

This commit is contained in:
Bill Zissimopoulos 2016-01-07 15:42:53 -08:00
parent 9b93df1788
commit 16895a5b68
5 changed files with 343 additions and 133 deletions

View File

@ -46,6 +46,7 @@ enum
{ {
FspFsctlTransactUnknownKind = 0, FspFsctlTransactUnknownKind = 0,
FspFsctlTransactCreateKind, FspFsctlTransactCreateKind,
FspFsctlTransactCreate2Kind,
FspFsctlTransactCleanupKind, FspFsctlTransactCleanupKind,
FspFsctlTransactCloseKind, FspFsctlTransactCloseKind,
FspFsctlTransactReadKind, FspFsctlTransactReadKind,
@ -120,6 +121,14 @@ typedef struct
UINT32 CaseSensitive:1; /* FileName comparisons should be case-sensitive */ UINT32 CaseSensitive:1; /* FileName comparisons should be case-sensitive */
} Create; } Create;
struct struct
{
UINT64 UserContext;
UINT64 UserContext2;
UINT32 CloseStatus; /* if non-0 close the file (and do NOT overwrite!) */
UINT32 FileAttributes; /* FILE_ATTRIBUTE_{NORMAL,DIRECTORY,etc.} */
UINT32 Supersede:1; /* 0: FILE_OVERWRITE operation, 1: FILE_SUPERSEDE operation */
} Create2;
struct
{ {
UINT64 UserContext; UINT64 UserContext;
UINT64 UserContext2; UINT64 UserContext2;
@ -169,6 +178,13 @@ typedef struct
FSP_FSCTL_TRANSACT_BUF FileName; /* file name to use for STATUS_REPARSE */ FSP_FSCTL_TRANSACT_BUF FileName; /* file name to use for STATUS_REPARSE */
} Reparse; } Reparse;
} Create; } Create;
struct
{
UINT64 UserContext; /* open file user context (unique file id) */
UINT64 UserContext2; /* kernel file object user context (only low 32 bits valid) */
UINT64 AllocationSize; /* file allocation size */
UINT64 FileSize; /* file size */
} Create2;
} Rsp; } Rsp;
FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[];
} FSP_FSCTL_TRANSACT_RSP; } FSP_FSCTL_TRANSACT_RSP;

View File

@ -60,19 +60,8 @@ static NTSTATUS FspFsvolCleanup(
UINT64 UserContext = FsContext->UserContext; UINT64 UserContext = FsContext->UserContext;
UINT64 UserContext2 = (UINT_PTR)FileObject->FsContext2; UINT64 UserContext2 = (UINT_PTR)FileObject->FsContext2;
FSP_FSCTL_TRANSACT_REQ *Request; FSP_FSCTL_TRANSACT_REQ *Request;
LONG OpenCount;
/* all handles on this FileObject are gone; close the FileObject */ FspFileContextClose(FsvolDeviceObject, FsContext);
OpenCount = FspFileContextClose(FsContext);
/* is the FsContext going away as well? */
if (0 == OpenCount)
{
/* remove the FsContext from the volume device generic table */
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
FspFsvolDeviceDeleteContext(FsvolDeviceObject, FsContext->UserContext, 0);
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
}
/* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */ /* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */
FspIopCreateRequestMustSucceed(Irp, FileNameRequired ? &FsContext->FileName : 0, 0, &Request); FspIopCreateRequestMustSucceed(Irp, FileNameRequired ? &FsContext->FileName : 0, 0, &Request);

View File

@ -292,10 +292,16 @@ NTSTATUS FspFsvolCreatePrepare(
PAGED_CODE(); PAGED_CODE();
NTSTATUS Result; NTSTATUS Result;
BOOLEAN Success;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PACCESS_STATE AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; PACCESS_STATE AccessState;
HANDLE UserModeAccessToken; HANDLE UserModeAccessToken;
PEPROCESS Process; PEPROCESS Process;
FSP_FILE_CONTEXT *FsContext;
if (FspFsctlTransactCreateKind == Request->Kind)
{
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
/* get a user-mode handle to the access token */ /* get a user-mode handle to the access token */
Result = ObOpenObjectByPointer(SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext), Result = ObOpenObjectByPointer(SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext),
@ -313,6 +319,45 @@ NTSTATUS FspFsvolCreatePrepare(
Request->Req.Create.AccessToken = (UINT_PTR)UserModeAccessToken; Request->Req.Create.AccessToken = (UINT_PTR)UserModeAccessToken;
return STATUS_SUCCESS; return STATUS_SUCCESS;
}
else if (FspFsctlTransactCreate2Kind == Request->Kind)
{
FsContext = FspIopRequestContext(Request, RequestFsContext);
/* lock the FsContext for Paging IO */
Success = FspFileContextPgioLockExclusive(FsContext, FALSE);
if (!Success)
{
/* repost the IRP to retry later */
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(IrpSp->DeviceObject);
if (FspIoqPostIrpNoCap(FsvolDeviceExtension->Ioq, Irp, &Result))
Result = STATUS_PENDING;
return Result;
}
/* see what the MM thinks about all this */
LARGE_INTEGER Zero = { 0 };
Success = MmCanFileBeTruncated(&FsContext->NonPaged->SectionObjectPointers, &Zero);
if (!Success)
{
/* cannot truncate; tell user mode that we are closing the file */
FspFileContextPgioUnlock(FsContext);
Request->Req.Create2.CloseStatus = (UINT32)STATUS_USER_MAPPED_FILE;
return STATUS_SUCCESS;
}
/* purge any caches on this file */
CcPurgeCacheSection(&FsContext->NonPaged->SectionObjectPointers, 0, 0, FALSE);
return STATUS_SUCCESS;
}
else
{
ASSERT(0);
return STATUS_INVALID_PARAMETER;
}
} }
VOID FspFsvolCreateComplete( VOID FspFsvolCreateComplete(
@ -325,14 +370,15 @@ VOID FspFsvolCreateComplete(
PFILE_OBJECT FileObject = IrpSp->FileObject; PFILE_OBJECT FileObject = IrpSp->FileObject;
SHARE_ACCESS TemporaryShareAccess; SHARE_ACCESS TemporaryShareAccess;
UNICODE_STRING ReparseFileName; UNICODE_STRING ReparseFileName;
FSP_FSCTL_TRANSACT_REQ *Request; FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
FSP_FILE_CONTEXT *FsContext; FSP_FILE_CONTEXT *FsContext = FspIopRequestContext(Request, RequestFsContext);
BOOLEAN Inserted;
if (FspFsctlTransactCreateKind == Request->Kind)
{
/* did the user-mode file system sent us a failure code? */ /* did the user-mode file system sent us a failure code? */
if (!NT_SUCCESS(Response->IoStatus.Status)) if (!NT_SUCCESS(Response->IoStatus.Status))
{ {
Irp->IoStatus.Information = (ULONG_PTR)Response->IoStatus.Information; Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status; Result = Response->IoStatus.Status;
FSP_RETURN(); FSP_RETURN();
} }
@ -395,41 +441,19 @@ VOID FspFsvolCreateComplete(
} }
/* get the FsContext from our Request and associate it with the Response UserContext */ /* get the FsContext from our Request and associate it with the Response UserContext */
Request = FspIrpRequest(Irp);
FsContext = FspIopRequestContext(Request, RequestFsContext);
FsContext->Header.AllocationSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize; FsContext->Header.AllocationSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize;
FsContext->Header.FileSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize; FsContext->Header.FileSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize;
FsContext->UserContext = Response->Rsp.Create.Opened.UserContext; FsContext->UserContext = Response->Rsp.Create.Opened.UserContext;
/* /* open the FsContext */
* Attempt to insert our FsContext into the volume device's generic table. FspIopRequestContext(Request, RequestFsContext) =
* If an FsContext with the same UserContext already exists, then use that FspFileContextOpen(FsvolDeviceObject, FsContext);
* FsContext instead.
*/
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
FsContext = FspFsvolDeviceInsertContext(FsvolDeviceObject,
FsContext->UserContext, FsContext, &FsContext->ElementStorage, &Inserted);
ASSERT(0 != FsContext);
if (Inserted)
/* Our FsContext was inserted into the volume device's generic table.
* Disassociate it from the Request.
*/
FspIopRequestContext(Request, RequestFsContext) = 0;
else
/*
* We are using a previously inserted FsContext. We must retain it.
* Our own FsContext is still associated with the Request and will be
* deleted during IRP completion.
*/
FspFileContextRetain(FsContext);
FspFileContextOpen(FsContext);
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
/* set up share access on FileObject; user-mode file system assumed to have done share check */ /* set up share access on FileObject; user-mode file system assumed to have done share check */
IoSetShareAccess(Response->Rsp.Create.Opened.GrantedAccess, IrpSp->Parameters.Create.ShareAccess, IoSetShareAccess(Response->Rsp.Create.Opened.GrantedAccess, IrpSp->Parameters.Create.ShareAccess,
FileObject, &TemporaryShareAccess); FileObject, &TemporaryShareAccess);
/* finish seting up the FileObject */ /* finish setting up the FileObject */
if (0 != FsvolDeviceExtension->FsvrtDeviceObject) if (0 != FsvolDeviceExtension->FsvrtDeviceObject)
FileObject->Vpb = FsvolDeviceExtension->FsvrtDeviceObject->Vpb; FileObject->Vpb = FsvolDeviceExtension->FsvrtDeviceObject->Vpb;
FileObject->SectionObjectPointer = &FsContext->NonPaged->SectionObjectPointers; FileObject->SectionObjectPointer = &FsContext->NonPaged->SectionObjectPointers;
@ -437,9 +461,90 @@ VOID FspFsvolCreateComplete(
FileObject->FsContext = FsContext; FileObject->FsContext = FsContext;
FileObject->FsContext2 = (PVOID)(UINT_PTR)Response->Rsp.Create.Opened.UserContext2; FileObject->FsContext2 = (PVOID)(UINT_PTR)Response->Rsp.Create.Opened.UserContext2;
if (FILE_SUPERSEDED == Response->IoStatus.Information ||
FILE_OVERWRITTEN == Response->IoStatus.Information)
{
/*
* Oh, noes! We have to go back to user mode to overwrite the file!
*/
/* delete the old request */
FspIopRequestContext(Request, RequestFsContext) = 0;
FspIrpRequest(Irp) = 0;
FspIopDeleteRequest(Request);
/* create the Create2 request; MustSucceed because we must either overwrite or close */
FspIopCreateRequestMustSucceed(Irp,
FsvolDeviceExtension->VolumeParams.FileNameRequired ? &FsContext->FileName : 0, 0,
&Request);
/* associate the FsContext with the Create2 request */
FspIopRequestContext(Request, RequestFsContext) = FsContext;
/* populate the Create2 request */
Request->Kind = FspFsctlTransactCreate2Kind;
Request->Req.Create2.UserContext = FsContext->UserContext;
Request->Req.Create2.UserContext2 = (UINT_PTR)FileObject->FsContext2;
Request->Req.Create2.FileAttributes = IrpSp->Parameters.Create.FileAttributes;
Request->Req.Create2.Supersede = FILE_SUPERSEDED == Response->IoStatus.Information;
Request->Req.Create2.CloseStatus = STATUS_SUCCESS;
/*
* Note that it is still possible for this request to not be delivered,
* if the volume device Ioq is stopped. But such failures are benign
* from our perspective, because they mean that the file system is going
* away and should correctly tear things down.
*/
if (FspIoqPostIrpNoCap(FsvolDeviceExtension->Ioq, Irp, &Result))
Result = STATUS_PENDING;
}
else
{
/* SUCCESS! */ /* SUCCESS! */
Irp->IoStatus.Information = (ULONG_PTR)Response->IoStatus.Information; Irp->IoStatus.Information = (ULONG_PTR)Response->IoStatus.Information;
Result = STATUS_SUCCESS;
}
}
else if (FspFsctlTransactCreate2Kind == Request->Kind)
{
/*
* A Create2 request will either succeed or else fail and close the corresponding file.
* There is no need to reach out to user-mode again and ask them to close the file.
*/
/* was this a close request? */
if (STATUS_SUCCESS != Request->Req.Create2.CloseStatus)
{
Irp->IoStatus.Information = 0;
Result = STATUS_SUCCESS != Request->Req.Create2.CloseStatus;
FSP_RETURN();
}
/* did the user-mode file system sent us a failure code? */
if (!NT_SUCCESS(Response->IoStatus.Status))
{
FspFileContextPgioUnlock(FsContext);
FspFileContextClose(FsvolDeviceObject, FsContext);
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status; Result = Response->IoStatus.Status;
FSP_RETURN();
}
/* file was successfully overwritten/superseded */
FsContext->Header.AllocationSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize;
FsContext->Header.FileSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize;
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&FsContext->Header.AllocationSize);
FspFileContextPgioUnlock(FsContext);
/* SUCCESS! */
Irp->IoStatus.Information = Request->Req.Create2.Supersede ? FILE_SUPERSEDED : FILE_OVERWRITTEN;
Result = STATUS_SUCCESS;
}
else
ASSERT(0);
FSP_LEAVE_IOC( FSP_LEAVE_IOC(
"FileObject=%p[%p:\"%wZ\"]", "FileObject=%p[%p:\"%wZ\"]",

View File

@ -527,12 +527,21 @@ typedef struct
NTSTATUS FspFileContextCreate(PDEVICE_OBJECT DeviceObject, NTSTATUS FspFileContextCreate(PDEVICE_OBJECT DeviceObject,
ULONG ExtraSize, FSP_FILE_CONTEXT **PFsContext); ULONG ExtraSize, FSP_FILE_CONTEXT **PFsContext);
VOID FspFileContextDelete(FSP_FILE_CONTEXT *FsContext); VOID FspFileContextDelete(FSP_FILE_CONTEXT *FsContext);
FSP_FILE_CONTEXT *FspFileContextOpen(PDEVICE_OBJECT FsvolDeviceObject,
FSP_FILE_CONTEXT *FsContext);
VOID FspFileContextClose(PDEVICE_OBJECT FsvolDeviceObject,
FSP_FILE_CONTEXT *FsContext);
static inline static inline
VOID FspFileContextRetain(FSP_FILE_CONTEXT *FsContext) VOID FspFileContextRetain(FSP_FILE_CONTEXT *FsContext)
{ {
InterlockedIncrement(&FsContext->RefCount); InterlockedIncrement(&FsContext->RefCount);
} }
static inline static inline
VOID FspFileContextRetain2(FSP_FILE_CONTEXT *FsContext)
{
InterlockedAdd(&FsContext->RefCount, 2);
}
static inline
VOID FspFileContextRelease(FSP_FILE_CONTEXT *FsContext) VOID FspFileContextRelease(FSP_FILE_CONTEXT *FsContext)
{ {
LONG RefCount = InterlockedDecrement(&FsContext->RefCount); LONG RefCount = InterlockedDecrement(&FsContext->RefCount);
@ -540,14 +549,34 @@ VOID FspFileContextRelease(FSP_FILE_CONTEXT *FsContext)
FspFileContextDelete(FsContext); FspFileContextDelete(FsContext);
} }
static inline static inline
VOID FspFileContextOpen(FSP_FILE_CONTEXT *FsContext) BOOLEAN FspFileContextLockShared(FSP_FILE_CONTEXT *FsContext, BOOLEAN Wait)
{ {
InterlockedIncrement(&FsContext->OpenCount); return ExAcquireResourceSharedLite(FsContext->Header.Resource, Wait);
} }
static inline static inline
LONG FspFileContextClose(FSP_FILE_CONTEXT *FsContext) BOOLEAN FspFileContextLockExclusive(FSP_FILE_CONTEXT *FsContext, BOOLEAN Wait)
{ {
return InterlockedDecrement(&FsContext->OpenCount); return ExAcquireResourceExclusiveLite(FsContext->Header.Resource, Wait);
}
static inline
VOID FspFileContextUnlock(FSP_FILE_CONTEXT *FsContext)
{
ExReleaseResourceLite(FsContext->Header.Resource);
}
static inline
BOOLEAN FspFileContextPgioLockShared(FSP_FILE_CONTEXT *FsContext, BOOLEAN Wait)
{
return ExAcquireResourceSharedLite(FsContext->Header.PagingIoResource, Wait);
}
static inline
BOOLEAN FspFileContextPgioLockExclusive(FSP_FILE_CONTEXT *FsContext, BOOLEAN Wait)
{
return ExAcquireResourceExclusiveLite(FsContext->Header.PagingIoResource, Wait);
}
static inline
VOID FspFileContextPgioUnlock(FSP_FILE_CONTEXT *FsContext)
{
ExReleaseResourceLite(FsContext->Header.PagingIoResource);
} }
/* debug */ /* debug */

View File

@ -72,3 +72,74 @@ VOID FspFileContextDelete(FSP_FILE_CONTEXT *FsContext)
FspFree(FsContext); FspFree(FsContext);
} }
FSP_FILE_CONTEXT *FspFileContextOpen(PDEVICE_OBJECT FsvolDeviceObject,
FSP_FILE_CONTEXT *FsContext)
{
/*
* Attempt to insert our FsContext into the volume device's generic table.
* If an FsContext with the same UserContext already exists, then use that
* FsContext instead.
*/
FSP_FILE_CONTEXT *OpenedFsContext;
BOOLEAN Inserted;
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
OpenedFsContext = FspFsvolDeviceInsertContext(FsvolDeviceObject,
FsContext->UserContext, FsContext, &FsContext->ElementStorage, &Inserted);
ASSERT(0 != OpenedFsContext);
if (Inserted)
{
/*
* The new FsContext was inserted into the Context table.
* Retain it. There should be (at least) two references to this FsContext,
* one from our caller and one from the Context table.
*/
ASSERT(OpenedFsContext == FsContext);
FspFileContextRetain(OpenedFsContext);
}
else
{
/*
* The new FsContext was NOT inserted into the Context table,
* instead a prior FsContext is being opened.
* Release the new FsContext since the caller will no longer reference it,
* and retain the prior FsContext TWICE, once for our caller and once for
* the Context table.
*/
ASSERT(OpenedFsContext != FsContext);
FspFileContextRetain2(OpenedFsContext);
}
InterlockedIncrement(&OpenedFsContext->OpenCount);
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
if (!Inserted)
FspFileContextRelease(FsContext);
return OpenedFsContext;
}
VOID FspFileContextClose(PDEVICE_OBJECT FsvolDeviceObject,
FSP_FILE_CONTEXT *FsContext)
{
/*
* Close the FsContext. If the OpenCount becomes zero remove it
* from the Context table.
*/
if (0 == InterlockedDecrement(&FsContext->OpenCount))
{
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
FspFsvolDeviceDeleteContext(FsvolDeviceObject, FsContext->UserContext, 0);
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
}
FspFileContextRelease(FsContext);
}