diff --git a/src/sys/create.c b/src/sys/create.c index 09540ac7..f738cece 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -66,6 +66,10 @@ static NTSTATUS FspFsvolCreate( (0 == IrpSp->FileObject->RelatedFileObject || 0 == IrpSp->FileObject->RelatedFileObject->FsContext)) { + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); + PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; + + IrpSp->FileObject->Vpb = FsvrtDeviceObject->Vpb; Irp->IoStatus.Information = FILE_OPENED; return STATUS_SUCCESS; } diff --git a/src/sys/driver.h b/src/sys/driver.h index c315cc10..bf3cfd2b 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -479,6 +479,17 @@ BOOLEAN FspValidRelativeSecurityDescriptor( NTSTATUS FspSecuritySubjectContextAccessCheck( PSECURITY_DESCRIPTOR SecurityDescriptor, ACCESS_MASK DesiredAccess, KPROCESSOR_MODE AccessMode); +/* delayed work queue */ +typedef struct +{ + KTIMER Timer; + KDPC Dpc; + WORK_QUEUE_ITEM WorkQueueItem; +} FSP_WORK_ITEM_WITH_DELAY; +VOID FspInitializeWorkItemWithDelay(FSP_WORK_ITEM_WITH_DELAY *WorkItem, + PWORKER_THREAD_ROUTINE Routine, PVOID Context); +VOID FspQueueWorkItemWithDelay(FSP_WORK_ITEM_WITH_DELAY *WorkItem, LARGE_INTEGER Timeout); + /* debug */ #if DBG BOOLEAN HasDbgBreakPoint(const char *Function); diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index cda06b21..c289d9f1 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -74,6 +74,7 @@ static NTSTATUS FspFsctlMountVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvrtDeleteVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +static WORKER_THREAD_ROUTINE FspFsvrtDeleteVolumeDelayed; static NTSTATUS FspFsvrtTransact( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsctlFileSystemControl( @@ -89,6 +90,7 @@ FSP_IOCMPL_DISPATCH FspFileSystemControlComplete; #pragma alloc_text(PAGE, FspFsctlCreateVolume) #pragma alloc_text(PAGE, FspFsctlMountVolume) #pragma alloc_text(PAGE, FspFsvrtDeleteVolume) +#pragma alloc_text(PAGE, FspFsvrtDeleteVolumeDelayed) #pragma alloc_text(PAGE, FspFsvrtTransact) #pragma alloc_text(PAGE, FspFsctlFileSystemControl) #pragma alloc_text(PAGE, FspFsvrtFileSystemControl) @@ -259,6 +261,13 @@ static NTSTATUS FspFsctlMountVolume( return Result; } +typedef struct +{ + PDEVICE_OBJECT FsvolDeviceObject; + PVPB OldVpb; + FSP_WORK_ITEM_WITH_DELAY WorkItemWithDelay; +} FSP_FSVRT_DELETE_VOLUME_WORK_ITEM; + static NTSTATUS FspFsvrtDeleteVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -272,8 +281,13 @@ static NTSTATUS FspFsvrtDeleteVolume( ExAcquireResourceExclusiveLite(&FsctlDeviceExtension->Base.Resource, TRUE); try { + PDEVICE_OBJECT FsctlDeviceObject = FsvrtDeviceExtension->FsctlDeviceObject; + PDEVICE_OBJECT FsvolDeviceObject = FsvrtDeviceExtension->FsvolDeviceObject; PVPB OldVpb; - BOOLEAN FreeVpb = FALSE; + BOOLEAN DeleteVpb = FALSE; + BOOLEAN DeleteDelayed = FALSE; + LARGE_INTEGER DelayTimeout; + FSP_FSVRT_DELETE_VOLUME_WORK_ITEM *WorkItem = 0; KIRQL Irql; /* access check */ @@ -282,6 +296,14 @@ static NTSTATUS FspFsvrtDeleteVolume( if (!NT_SUCCESS(Result)) goto exit; + /* pre-allocate a work item in case we need it for delayed delete */ + WorkItem = FspAllocNonPaged(sizeof *WorkItem); + if (0 == WorkItem) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + /* mark the virtual volume device as deleted */ FsvrtDeviceExtension->Deleted = TRUE; @@ -302,16 +324,21 @@ static NTSTATUS FspFsvrtDeleteVolume( DeviceObject->Vpb->RealDevice = OldVpb->RealDevice; DeviceObject->Vpb->RealDevice->Vpb = DeviceObject->Vpb; FsvrtDeviceExtension->SwapVpb = 0; - FreeVpb = 0 == OldVpb->ReferenceCount; + DeleteVpb = 0 == OldVpb->ReferenceCount; + DeleteDelayed = !DeleteVpb && 0 != FsvolDeviceObject; + if (DeleteDelayed) + /* keep VPB around for delayed delete */ + OldVpb->ReferenceCount++; } IoReleaseVpbSpinLock(Irql); - if (FreeVpb) + if (DeleteDelayed) + /* keep fsvol around for delayed delete */ + FspDeviceRetain(FsvolDeviceObject); + else if (DeleteVpb) FspFreeExternal(OldVpb); #pragma prefast(pop) /* release the file system device and virtual volume objects */ - PDEVICE_OBJECT FsctlDeviceObject = FsvrtDeviceExtension->FsctlDeviceObject; - PDEVICE_OBJECT FsvolDeviceObject = FsvrtDeviceExtension->FsvolDeviceObject; FsvrtDeviceExtension->FsvolDeviceObject = 0; if (0 != FsvolDeviceObject) FspDeviceRelease(FsvolDeviceObject); @@ -319,9 +346,23 @@ static NTSTATUS FspFsvrtDeleteVolume( FspFsctlDeviceVolumeDeleted(FsctlDeviceObject); + /* are we doing delayed delete of VPB and fsvol? */ + if (DeleteDelayed) + { + DelayTimeout.QuadPart = 300/*ms*/ * -10000; + WorkItem->FsvolDeviceObject = FsvolDeviceObject; + WorkItem->OldVpb = OldVpb; + FspInitializeWorkItemWithDelay(&WorkItem->WorkItemWithDelay, + FspFsvrtDeleteVolumeDelayed, WorkItem); + FspQueueWorkItemWithDelay(&WorkItem->WorkItemWithDelay, DelayTimeout); + WorkItem = 0; + } + Result = STATUS_SUCCESS; - exit:; + exit: + if (0 != WorkItem) + FspFree(WorkItem); } finally { @@ -331,6 +372,34 @@ static NTSTATUS FspFsvrtDeleteVolume( return Result; } +static VOID FspFsvrtDeleteVolumeDelayed(PVOID Context) +{ + PAGED_CODE(); + + FSP_FSVRT_DELETE_VOLUME_WORK_ITEM *WorkItem = Context; + BOOLEAN DeleteVpb = FALSE; + LARGE_INTEGER DelayTimeout; + KIRQL Irql; + + IoAcquireVpbSpinLock(&Irql); + ASSERT(0 != WorkItem->OldVpb->ReferenceCount); + DeleteVpb = 1 == WorkItem->OldVpb->ReferenceCount; + if (DeleteVpb) + WorkItem->OldVpb->ReferenceCount = 0; + IoReleaseVpbSpinLock(Irql); + if (DeleteVpb) + { + FspFreeExternal(WorkItem->OldVpb); + FspDeviceRelease(WorkItem->FsvolDeviceObject); + FspFree(WorkItem); + } + else + { + DelayTimeout.QuadPart = 300/*ms*/ * -10000; + FspQueueWorkItemWithDelay(&WorkItem->WorkItemWithDelay, DelayTimeout); + } +} + static NTSTATUS FspFsvrtTransact( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { diff --git a/src/sys/misc.c b/src/sys/misc.c index d4f12530..c4cd398a 100644 --- a/src/sys/misc.c +++ b/src/sys/misc.c @@ -12,11 +12,17 @@ BOOLEAN FspValidRelativeSecurityDescriptor( SECURITY_INFORMATION RequiredInformation); NTSTATUS FspSecuritySubjectContextAccessCheck( PSECURITY_DESCRIPTOR SecurityDescriptor, ACCESS_MASK DesiredAccess, KPROCESSOR_MODE AccessMode); +VOID FspInitializeWorkItemWithDelay(FSP_WORK_ITEM_WITH_DELAY *WorkItem, + PWORKER_THREAD_ROUTINE Routine, PVOID Context); +VOID FspQueueWorkItemWithDelay(FSP_WORK_ITEM_WITH_DELAY *WorkItem, LARGE_INTEGER Timeout); +static KDEFERRED_ROUTINE FspQueueWorkItemWithDelayDPC; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspCreateGuid) #pragma alloc_text(PAGE, FspValidRelativeSecurityDescriptor) #pragma alloc_text(PAGE, FspSecuritySubjectContextAccessCheck) +#pragma alloc_text(PAGE, FspInitializeWorkItemWithDelay) +#pragma alloc_text(PAGE, FspQueueWorkItemWithDelay) #endif NTSTATUS FspCreateGuid(GUID *Guid) @@ -74,3 +80,30 @@ NTSTATUS FspSecuritySubjectContextAccessCheck( return Result; } + +VOID FspInitializeWorkItemWithDelay(FSP_WORK_ITEM_WITH_DELAY *WorkItem, + PWORKER_THREAD_ROUTINE Routine, PVOID Context) +{ + PAGED_CODE(); + + KeInitializeTimer(&WorkItem->Timer); + KeInitializeDpc(&WorkItem->Dpc, FspQueueWorkItemWithDelayDPC, WorkItem); + ExInitializeWorkItem(&WorkItem->WorkQueueItem, Routine, Context); +} + +VOID FspQueueWorkItemWithDelay(FSP_WORK_ITEM_WITH_DELAY *WorkItem, LARGE_INTEGER Timeout) +{ + PAGED_CODE(); + + KeSetTimer(&WorkItem->Timer, Timeout, &WorkItem->Dpc); +} + +static VOID FspQueueWorkItemWithDelayDPC(PKDPC Dpc, + PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) +{ + // !PAGED_CODE(); + + FSP_WORK_ITEM_WITH_DELAY *WorkItem = DeferredContext; + + ExQueueWorkItem(&WorkItem->WorkQueueItem, DelayedWorkQueue); +}