diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index ea7dc788..fba11374 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -13,6 +13,8 @@ static NTSTATUS FspFsvrtCleanup( static NTSTATUS FspFsvolCleanup( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolCleanupComplete; +static FSP_IOP_REQUEST_FINI FspFsvolCleanupRequestFini; +static VOID FspFsvolCleanupUninitialize(PVOID Context); FSP_DRIVER_DISPATCH FspCleanup; #ifdef ALLOC_PRAGMA @@ -20,9 +22,25 @@ FSP_DRIVER_DISPATCH FspCleanup; #pragma alloc_text(PAGE, FspFsvrtCleanup) #pragma alloc_text(PAGE, FspFsvolCleanup) #pragma alloc_text(PAGE, FspFsvolCleanupComplete) +#pragma alloc_text(PAGE, FspFsvolCleanupRequestFini) +#pragma alloc_text(PAGE, FspFsvolCleanupUninitialize) #pragma alloc_text(PAGE, FspCleanup) #endif +enum +{ + /* Cleanup */ + RequestDeviceObject = 0, + RequestIrp = 1, +}; + +typedef struct +{ + PFILE_OBJECT FileObject; + LARGE_INTEGER TruncateSize, *PTruncateSize; + WORK_QUEUE_ITEM WorkQueueItem; +} FSP_FSVOL_CLEANUP_UNINITIALIZE_WORK_ITEM; + static NTSTATUS FspFsctlCleanup( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -61,22 +79,31 @@ static NTSTATUS FspFsvolCleanup( ASSERT(FileNode == FileDesc->FileNode); - /* !!!: REVISIT! */ - FspFileNodeClose(FileNode, FileObject, &DeletePending); - CcUninitializeCacheMap(FileObject, 0, 0); - - /* - * If DeletePending is TRUE, the FileNode is no longer in the Context table, - * therefore we do not need to protect its FileName against renames! - */ + FspFileNodeCleanup(FileNode, FileObject, &DeletePending); + if (DeletePending) + { + FspFsvolDeviceFileRenameAcquireShared(FsvolDeviceObject); + FspFileNodeAcquireExclusive(FileNode, Full); + } + else + FspFileNodeAcquireShared(FileNode, Full); /* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */ - FspIopCreateRequestMustSucceed(Irp, DeletePending ? &FileNode->FileName : 0, 0, &Request); + FspIopCreateRequestMustSucceedEx(Irp, DeletePending ? &FileNode->FileName : 0, 0, + FspFsvolCleanupRequestFini, &Request); Request->Kind = FspFsctlTransactCleanupKind; Request->Req.Cleanup.UserContext = FileNode->UserContext; Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2; Request->Req.Cleanup.Delete = DeletePending; + if (DeletePending) + { + FspFsvolDeviceFileRenameSetOwner(FsvolDeviceObject, Request); + FspIopRequestContext(Request, RequestDeviceObject) = FsvolDeviceObject; + } + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestIrp) = Irp; + return FSP_STATUS_IOQ_POST_BEST_EFFORT; /* @@ -95,6 +122,117 @@ NTSTATUS FspFsvolCleanupComplete( FSP_LEAVE_IOC("FileObject=%p", IrpSp->FileObject); } +static VOID FspFsvolCleanupRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) +{ + /* + * Cleanup is rather unusual in that we are doing the cleanup post-processing + * in RequestFini rather than in CleanupComplete. The reason for this is that + * we want this processing to happen even in the (unlikely) event of the user- + * mode file system going away, while our Request is queued (in which case the + * Irp will get cancelled). + */ + + PAGED_CODE(); + + PDEVICE_OBJECT FsvolDeviceObject = Context[RequestDeviceObject]; + PIRP Irp = Context[RequestIrp]; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FSCTL_FILE_INFO FileInfo; + LARGE_INTEGER TruncateSize = { 0 }, *PTruncateSize = 0; + BOOLEAN DeletePending = 0 != Request->Req.Cleanup.Delete; + BOOLEAN DeletedFromContextTable; + BOOLEAN Success; + + FspFileNodeClose(FileNode, FileObject, &DeletedFromContextTable); + if (DeletedFromContextTable) + { + if (DeletePending) + PTruncateSize = &TruncateSize; + else if (FileNode->TruncateOnClose) + { + /* + * Even when FileInfoTimeout != Infinity, + * this is the last size that the cache manager knows. + */ + FspFileNodeGetFileInfo(FileNode, &FileInfo); + + TruncateSize.QuadPart = FileInfo.FileSize; + PTruncateSize = &TruncateSize; + } + } + + if (DeletePending) + { + ASSERT(0 != FsvolDeviceObject); + + /* FileNode is Exclusive Full; release Pgio */ + FspFileNodeReleaseOwner(FileNode, Pgio, Request); + + /* FileNode is now Exclusive Main; owner is Request */ + } + else + { + ASSERT(0 == FsvolDeviceObject); + + /* FileNode is Shared Full; reacquire as Exclusive Main for CcUnitializeCacheMap */ + FspFileNodeReleaseOwner(FileNode, Full, Request); + + Success = DEBUGRANDTEST(90, TRUE) && FspFileNodeTryAcquireExclusive(FileNode, Main); + if (!Success) + { + /* oh, maaan! we now have to do delayed uninitialize! */ + + FSP_FSVOL_CLEANUP_UNINITIALIZE_WORK_ITEM *WorkItem; + + WorkItem = FspAllocatePoolMustSucceed( + NonPagedPool, sizeof *WorkItem, FSP_ALLOC_INTERNAL_TAG); + WorkItem->FileObject = FileObject; + WorkItem->TruncateSize = TruncateSize; + WorkItem->PTruncateSize = 0 != PTruncateSize ? &WorkItem->TruncateSize : 0; + ExInitializeWorkItem(&WorkItem->WorkQueueItem, FspFsvolCleanupUninitialize, WorkItem); + + /* make sure that the file object (and corresponding device object) stay around! */ + ObReferenceObject(FileObject); + + ExQueueWorkItem(&WorkItem->WorkQueueItem, CriticalWorkQueue); + + return; + } + + /* FileNode is now Exclusive Main; owner is current thread */ + } + + CcUninitializeCacheMap(FileObject, PTruncateSize, 0); + + /* this works correctly even if owner is current thread */ + FspFileNodeReleaseOwner(FileNode, Main, Request); + + if (DeletePending) + FspFsvolDeviceFileRenameReleaseOwner(FsvolDeviceObject, Request); +} + +static VOID FspFsvolCleanupUninitialize(PVOID Context) +{ + PAGED_CODE(); + + FSP_FSVOL_CLEANUP_UNINITIALIZE_WORK_ITEM *WorkItem = Context; + PFILE_OBJECT FileObject = WorkItem->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + LARGE_INTEGER *PTruncateSize = WorkItem->PTruncateSize; + + FspFileNodeAcquireExclusive(FileNode, Main); + + CcUninitializeCacheMap(FileObject, PTruncateSize, 0); + + FspFileNodeRelease(FileNode, Main); + + ObDereferenceObject(FileObject); + + FspFree(WorkItem); +} + NTSTATUS FspCleanup( PDEVICE_OBJECT DeviceObject, PIRP Irp) { diff --git a/src/sys/driver.h b/src/sys/driver.h index bea8b85d..6b18dbc0 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -538,6 +538,8 @@ enum FspIopCreateRequestFunnel(I, F, E, 0, FspIopRequestMustSucceed, P) #define FspIopCreateRequestEx(I, F, E, RF, P)\ FspIopCreateRequestFunnel(I, F, E, RF, 0, P) +#define FspIopCreateRequestMustSucceedEx(I, F, E, RF, P)\ + FspIopCreateRequestFunnel(I, F, E, RF, FspIopRequestMustSucceed, P) #define FspIopCreateRequestWorkItem(I, E, RF, P)\ FspIopCreateRequestFunnel(I, 0, E, RF, FspIopRequestNonPaged, P) #define FspIopRequestContext(Request, I)\ @@ -765,6 +767,7 @@ typedef struct NTSTATUS CcStatus; UINT64 Security; ULONG SecurityChangeNumber; + BOOLEAN TruncateOnClose; /* read-only after creation (and insertion in the ContextTable) */ PDEVICE_OBJECT FsvolDeviceObject; UINT64 UserContext; @@ -803,8 +806,10 @@ VOID FspFileNodeReleaseF(FSP_FILE_NODE *FileNode, ULONG Flags); VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner); FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, UINT32 GrantedAccess, UINT32 ShareAccess, NTSTATUS *PResult); -VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, +VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending); +VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, + PBOOLEAN PDeletedFromContextTable); VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName); VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo); BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo); diff --git a/src/sys/file.c b/src/sys/file.c index 4baa912b..6ebff8eb 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -18,8 +18,10 @@ VOID FspFileNodeReleaseF(FSP_FILE_NODE *FileNode, ULONG Flags); VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner); FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, UINT32 GrantedAccess, UINT32 ShareAccess, NTSTATUS *PResult); -VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, +VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending); +VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, + PBOOLEAN PDeletedFromContextTable); VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName); VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo); BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo); @@ -45,6 +47,7 @@ VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc); #pragma alloc_text(PAGE, FspFileNodeReleaseF) #pragma alloc_text(PAGE, FspFileNodeReleaseOwnerF) #pragma alloc_text(PAGE, FspFileNodeOpen) +#pragma alloc_text(PAGE, FspFileNodeCleanup) #pragma alloc_text(PAGE, FspFileNodeClose) #pragma alloc_text(PAGE, FspFileNodeRename) #pragma alloc_text(PAGE, FspFileNodeGetFileInfo) @@ -380,8 +383,38 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, return OpenedFileNode; } -VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, +VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending) +{ + /* + * Determine whether a FileNode should be deleted. Note that when + * FileNode->DeletePending is set, the OpenCount cannot be changed + * because FspFileNodeOpen() will return STATUS_DELETE_PENDING. + */ + + PAGED_CODE(); + + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + BOOLEAN DeletePending, SingleOpen; + + FspFsvolDeviceLockContextTable(FsvolDeviceObject); + + if (FileDesc->DeleteOnClose) + FileNode->DeletePending = TRUE; + DeletePending = 0 != FileNode->DeletePending; + MemoryBarrier(); + + SingleOpen = 1 == FileNode->OpenCount; + + FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); + + if (0 != PDeletePending) + *PDeletePending = SingleOpen && DeletePending; +} + +VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, + PBOOLEAN PDeletedFromContextTable) { /* * Close the FileNode. If the OpenCount becomes zero remove it @@ -391,16 +424,10 @@ VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PAGED_CODE(); PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; - FSP_FILE_DESC *FileDesc = FileObject->FsContext2; - BOOLEAN Deleted = FALSE, DeletePending; + BOOLEAN Deleted = FALSE; FspFsvolDeviceLockContextTable(FsvolDeviceObject); - if (FileDesc->DeleteOnClose) - FileNode->DeletePending = TRUE; - DeletePending = 0 != FileNode->DeletePending; - MemoryBarrier(); - IoRemoveShareAccess(FileObject, &FileNode->ShareAccess); if (0 == --FileNode->OpenCount) FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName, &Deleted); @@ -410,8 +437,8 @@ VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, if (Deleted) FspFileNodeDereference(FileNode); - if (0 != PDeletePending) - *PDeletePending = Deleted && DeletePending; + if (0 != PDeletedFromContextTable) + *PDeletedFromContextTable = Deleted; } VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName) diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 72f766ba..cffc3218 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -593,6 +593,7 @@ static NTSTATUS FspFsvolSetAllocationInformation(PFILE_OBJECT FileObject, FSP_FILE_NODE *FileNode = FileObject->FsContext; FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo); + FileNode->TruncateOnClose = TRUE; } return STATUS_SUCCESS; @@ -666,6 +667,7 @@ static NTSTATUS FspFsvolSetEndOfFileInformation(PFILE_OBJECT FileObject, FSP_FILE_NODE *FileNode = FileObject->FsContext; FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo); + FileNode->TruncateOnClose = TRUE; } return STATUS_SUCCESS;