diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index 27285a8b..6560fb6a 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -61,7 +61,7 @@ static NTSTATUS FspFsvolCleanup( UINT64 UserContext2 = (UINT_PTR)FileObject->FsContext2; FSP_FSCTL_TRANSACT_REQ *Request; - FspFileContextClose(FsvolDeviceObject, FsContext); + FspFileContextClose(FsContext, FileObject); /* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */ FspIopCreateRequestMustSucceed(Irp, FileNameRequired ? &FsContext->FileName : 0, 0, &Request); diff --git a/src/sys/create.c b/src/sys/create.c index 9f4f276a..5fbb4948 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -14,6 +14,9 @@ static NTSTATUS FspFsvolCreate( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOPREP_DISPATCH FspFsvolCreatePrepare; FSP_IOCMPL_DISPATCH FspFsvolCreateComplete; +static VOID FspFsvolCreatePostClose( + PDEVICE_OBJECT FsvolDeviceObject, PUNICODE_STRING FileName, + UINT64 UserContext, UINT64 UserContext2, NTSTATUS CloseStatus); static FSP_IOP_REQUEST_FINI FspFsvolCreateRequestFini; FSP_DRIVER_DISPATCH FspCreate; @@ -23,6 +26,7 @@ FSP_DRIVER_DISPATCH FspCreate; #pragma alloc_text(PAGE, FspFsvolCreate) #pragma alloc_text(PAGE, FspFsvolCreatePrepare) #pragma alloc_text(PAGE, FspFsvolCreateComplete) +#pragma alloc_text(PAGE, FspFsvolCreatePostClose) #pragma alloc_text(PAGE, FspFsvolCreateRequestFini) #pragma alloc_text(PAGE, FspCreate) #endif @@ -368,10 +372,10 @@ VOID FspFsvolCreateComplete( PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PFILE_OBJECT FileObject = IrpSp->FileObject; - SHARE_ACCESS TemporaryShareAccess; - UNICODE_STRING ReparseFileName; FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); FSP_FILE_CONTEXT *FsContext = FspIopRequestContext(Request, RequestFsContext); + UNICODE_STRING ReparseFileName; + BOOLEAN DeleteOnClose; if (FspFsctlTransactCreateKind == Request->Kind) { @@ -445,15 +449,7 @@ VOID FspFsvolCreateComplete( FsContext->Header.FileSize.QuadPart = Response->Rsp.Create.Opened.AllocationSize; FsContext->UserContext = Response->Rsp.Create.Opened.UserContext; - /* open the FsContext */ - FspIopRequestContext(Request, RequestFsContext) = - FspFileContextOpen(FsvolDeviceObject, FsContext); - - /* 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, - FileObject, &TemporaryShareAccess); - - /* finish setting up the FileObject */ + /* set up the FileObject */ if (0 != FsvolDeviceExtension->FsvrtDeviceObject) FileObject->Vpb = FsvolDeviceExtension->FsvrtDeviceObject->Vpb; FileObject->SectionObjectPointer = &FsContext->NonPaged->SectionObjectPointers; @@ -461,6 +457,59 @@ VOID FspFsvolCreateComplete( FileObject->FsContext = FsContext; FileObject->FsContext2 = (PVOID)(UINT_PTR)Response->Rsp.Create.Opened.UserContext2; + DeleteOnClose = BooleanFlagOn(Request->Req.Create.CreateOptions, FILE_DELETE_ON_CLOSE); + + /* open the FsContext */ + FsContext = FspFileContextOpen(FsContext, FileObject, + Response->Rsp.Create.Opened.GrantedAccess, IrpSp->Parameters.Create.ShareAccess, + &Result); + FspIopRequestContext(Request, RequestFsContext) = FsContext; + if (0 == FsContext) + { + /* unable to open the FsContext; post a close Create2 request */ + FspFsvolCreatePostClose(FsvolDeviceObject, + FsvolDeviceExtension->VolumeParams.FileNameRequired ? &FsContext->FileName : 0, + Response->Rsp.Create.Opened.UserContext, + Response->Rsp.Create.Opened.UserContext2, + Result); + + FSP_RETURN(); + } + + if (FILE_OPENED == Response->IoStatus.Information) + { + /* + * FastFat quote: + * If the user wants write access access to the file make sure there + * is not a process mapping this file as an image. Any attempt to + * delete the file will be stopped in fileinfo.c + * + * If the user wants to delete on close, we must check at this + * point though. + */ + if (FlagOn(Response->Rsp.Create.Opened.GrantedAccess, FILE_WRITE_DATA) || + DeleteOnClose) + { + if (!MmFlushImageSection(&FsContext->NonPaged->SectionObjectPointers, + MmFlushForWrite)) + { + Result = DeleteOnClose ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION; + + FspFsvolCreatePostClose(FsvolDeviceObject, + FsvolDeviceExtension->VolumeParams.FileNameRequired ? &FsContext->FileName : 0, + Response->Rsp.Create.Opened.UserContext, + Response->Rsp.Create.Opened.UserContext2, + Result); + + FSP_RETURN(); + } + } + + /* SUCCESS! */ + Irp->IoStatus.Information = (ULONG_PTR)Response->IoStatus.Information; + Result = STATUS_SUCCESS; + } + else if (FILE_SUPERSEDED == Response->IoStatus.Information || FILE_OVERWRITTEN == Response->IoStatus.Information) { @@ -525,7 +574,7 @@ VOID FspFsvolCreateComplete( if (!NT_SUCCESS(Response->IoStatus.Status)) { FspFileContextPgioUnlock(FsContext); - FspFileContextClose(FsvolDeviceObject, FsContext); + FspFileContextClose(FsContext, FileObject); Irp->IoStatus.Information = 0; Result = Response->IoStatus.Status; @@ -551,6 +600,39 @@ VOID FspFsvolCreateComplete( IrpSp->FileObject, IrpSp->FileObject->RelatedFileObject, IrpSp->FileObject->FileName); } +static VOID FspFsvolCreatePostClose( + PDEVICE_OBJECT FsvolDeviceObject, PUNICODE_STRING FileName, + UINT64 UserContext, UINT64 UserContext2, NTSTATUS CloseStatus) +{ + PAGED_CODE(); + + ASSERT(STATUS_SUCCESS != CloseStatus); + + FSP_FSCTL_TRANSACT_REQ *Request; + + /* create the user-mode file system request; MustSucceed because we cannot fail */ + FspIopCreateRequestMustSucceed(0, FileName, 0, &Request); + + /* populate the Create2 request */ + Request->Kind = FspFsctlTransactCreate2Kind; + Request->Req.Create2.UserContext = UserContext; + Request->Req.Create2.UserContext2 = UserContext2; + Request->Req.Create2.CloseStatus = CloseStatus; + + /* + * Post as a BestEffort work request. This allows us to complete our own IRP + * and return immediately. + */ + FspIopPostWorkRequestBestEffort(FsvolDeviceObject, Request); + + /* + * 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. + */ +} + static VOID FspFsvolCreateRequestFini(PVOID Context[3]) { PAGED_CODE(); diff --git a/src/sys/driver.h b/src/sys/driver.h index 6cd8c431..6b934b14 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -517,6 +517,8 @@ typedef struct /* interlocked access */ LONG RefCount; LONG OpenCount; + /* locked access */ + SHARE_ACCESS ShareAccess; /* read-only after creation (and insertion in the GenericTable) */ PDEVICE_OBJECT FsvolDeviceObject; UINT64 UserContext; @@ -527,21 +529,15 @@ typedef struct NTSTATUS FspFileContextCreate(PDEVICE_OBJECT DeviceObject, ULONG ExtraSize, FSP_FILE_CONTEXT **PFsContext); 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); +FSP_FILE_CONTEXT *FspFileContextOpen(FSP_FILE_CONTEXT *FsContext, PFILE_OBJECT FileObject, + DWORD GrantedAccess, DWORD ShareAccess, NTSTATUS *PResult); +VOID FspFileContextClose(FSP_FILE_CONTEXT *FsContext, PFILE_OBJECT FileObject); static inline VOID FspFileContextRetain(FSP_FILE_CONTEXT *FsContext) { InterlockedIncrement(&FsContext->RefCount); } static inline -VOID FspFileContextRetain2(FSP_FILE_CONTEXT *FsContext) -{ - InterlockedAdd(&FsContext->RefCount, 2); -} -static inline VOID FspFileContextRelease(FSP_FILE_CONTEXT *FsContext) { LONG RefCount = InterlockedDecrement(&FsContext->RefCount); diff --git a/src/sys/filectx.c b/src/sys/filectx.c index 743e5846..b648f09e 100644 --- a/src/sys/filectx.c +++ b/src/sys/filectx.c @@ -73,8 +73,8 @@ VOID FspFileContextDelete(FSP_FILE_CONTEXT *FsContext) FspFree(FsContext); } -FSP_FILE_CONTEXT *FspFileContextOpen(PDEVICE_OBJECT FsvolDeviceObject, - FSP_FILE_CONTEXT *FsContext) +FSP_FILE_CONTEXT *FspFileContextOpen(FSP_FILE_CONTEXT *FsContext, PFILE_OBJECT FileObject, + DWORD GrantedAccess, DWORD ShareAccess, NTSTATUS *PResult) { /* * Attempt to insert our FsContext into the volume device's generic table. @@ -82,6 +82,7 @@ FSP_FILE_CONTEXT *FspFileContextOpen(PDEVICE_OBJECT FsvolDeviceObject, * FsContext instead. */ + PDEVICE_OBJECT FsvolDeviceObject = FsContext->FsvolDeviceObject; FSP_FILE_CONTEXT *OpenedFsContext; BOOLEAN Inserted; @@ -94,29 +95,58 @@ FSP_FILE_CONTEXT *FspFileContextOpen(PDEVICE_OBJECT FsvolDeviceObject, 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. + * The new FsContext was inserted into the Context table. Set its share access + * and retain and open it. There should be (at least) two references to this + * FsContext, one from our caller and one from the Context table. */ ASSERT(OpenedFsContext == FsContext); + IoSetShareAccess(GrantedAccess, ShareAccess, FileObject, &FsContext->ShareAccess); FspFileContextRetain(OpenedFsContext); + OpenedFsContext->OpenCount++; } 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. + * The new FsContext was NOT inserted into the Context table. Instead we are + * opening a prior FsContext that we found in the table. + * + * First check and update the share access. If successful then retain the + * prior FsContext for our caller and release the original FsContext. */ ASSERT(OpenedFsContext != FsContext); - FspFileContextRetain2(OpenedFsContext); - } + /* + * FastFat says to do the following on Vista and above. + * + * Quote: + * Do an extra test for writeable user sections if the user did not allow + * write sharing - this is neccessary since a section may exist with no handles + * open to the file its based against. + */ + NTSTATUS Result = STATUS_SUCCESS; + if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) && + FlagOn(GrantedAccess, + FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE) && + MmDoesFileHaveUserWritableReferences(&FsContext->NonPaged->SectionObjectPointers)) + Result = STATUS_SHARING_VIOLATION; - InterlockedIncrement(&OpenedFsContext->OpenCount); + /* share access check */ + if (NT_SUCCESS(Result)) + Result = IoCheckShareAccess(GrantedAccess, ShareAccess, FileObject, &FsContext->ShareAccess, TRUE); + if (NT_SUCCESS(Result)) + { + FspFileContextRetain(OpenedFsContext); + OpenedFsContext->OpenCount++; + } + else + { + if (0 != PResult) + *PResult = Result; + + OpenedFsContext = 0; + } + } FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); @@ -126,20 +156,24 @@ FSP_FILE_CONTEXT *FspFileContextOpen(PDEVICE_OBJECT FsvolDeviceObject, return OpenedFsContext; } -VOID FspFileContextClose(PDEVICE_OBJECT FsvolDeviceObject, - FSP_FILE_CONTEXT *FsContext) +VOID FspFileContextClose(FSP_FILE_CONTEXT *FsContext, PFILE_OBJECT FileObject) { /* * 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); - } + PDEVICE_OBJECT FsvolDeviceObject = FsContext->FsvolDeviceObject; + BOOLEAN Deleted = FALSE; - FspFileContextRelease(FsContext); + FspFsvolDeviceLockContextTable(FsvolDeviceObject); + + IoRemoveShareAccess(FileObject, &FsContext->ShareAccess); + if (0 == --FsContext->OpenCount) + FspFsvolDeviceDeleteContext(FsvolDeviceObject, FsContext->UserContext, &Deleted); + + FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); + + if (Deleted) + FspFileContextRelease(FsContext); }