/*++ Copyright (c) 1999 Microsoft Corporation Module Name: volsnap.cxx Abstract: This driver provides volume snapshot capabilities. Author: Norbert P. Kusters (norbertk) 22-Jan-1999 Environment: kernel mode only Notes: Revision History: --*/ extern "C" { #define RTL_USE_AVL_TABLES 0 #include #include #include #include #include #include #include #include #include #include #include #include #ifndef IRP_HIGH_PRIORITY_PAGING_IO #define IRP_HIGH_PRIORITY_PAGING_IO 0x00008000 #endif #if defined(_WIN64) #define ERROR_LOG_ENTRY_SIZE (0x30) #else #define ERROR_LOG_ENTRY_SIZE (0x20) #endif static const SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTKERNELAPI KPRIORITY KeQueryPriorityThread ( IN PKTHREAD Thread ); BOOLEAN VspIsSetup( ); } #define OLD_IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS CTL_CODE(VOLSNAPCONTROLTYPE, 6, METHOD_BUFFERED, FILE_READ_ACCESS) #define OLD_IOCTL_VOLSNAP_QUERY_DIFF_AREA CTL_CODE(VOLSNAPCONTROLTYPE, 9, METHOD_BUFFERED, FILE_READ_ACCESS) #define OLD_IOCTL_VOLSNAP_QUERY_DIFF_AREA_SIZES CTL_CODE(VOLSNAPCONTROLTYPE, 11, METHOD_BUFFERED, FILE_READ_ACCESS) #define OLD_IOCTL_VOLSNAP_QUERY_ORIGINAL_VOLUME_NAME CTL_CODE(VOLSNAPCONTROLTYPE, 100, METHOD_BUFFERED, FILE_READ_ACCESS) #define OLD_IOCTL_VOLSNAP_QUERY_CONFIG_INFO CTL_CODE(VOLSNAPCONTROLTYPE, 101, METHOD_BUFFERED, FILE_READ_ACCESS) #define OLD_IOCTL_VOLSNAP_QUERY_APPLICATION_INFO CTL_CODE(VOLSNAPCONTROLTYPE, 103, METHOD_BUFFERED, FILE_READ_ACCESS) ULONG VsErrorLogSequence = 0; VOID VspWriteVolume( IN PVOID Context ); VOID VspWorkerThread( IN PVOID RootExtension ); VOID VspCleanupInitialSnapshot( IN PVOLUME_EXTENSION Extension, IN BOOLEAN NeedLock, IN BOOLEAN IsFinalRemove ); VOID VspWriteVolumePhase1( IN PVOID TableEntry ); VOID VspFreeCopyIrp( IN PVOLUME_EXTENSION Extension, IN PIRP CopyIrp ); NTSTATUS VspAbortPreparedSnapshot( IN PFILTER_EXTENSION Filter, IN BOOLEAN NeedLock, IN BOOLEAN IsFinalRemove ); NTSTATUS VspReleaseWrites( IN PFILTER_EXTENSION Filter ); NTSTATUS VspSetApplicationInfo( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ); NTSTATUS VspWriteContextCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Filter ); NTSTATUS VspRefCountCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Filter ); VOID VspCleanupFilter( IN PFILTER_EXTENSION Filter, IN BOOLEAN IsOffline, IN BOOLEAN IsFinalRemove ); VOID VspDecrementRefCount( IN PFILTER_EXTENSION Filter ); NTSTATUS VspSignalCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Event ); NTSTATUS VspMarkFreeSpaceInBitmap( IN PVOLUME_EXTENSION Extension, IN HANDLE UseThisHandle, IN PRTL_BITMAP BitmapToSet ); VOID VspAndBitmaps( IN OUT PRTL_BITMAP BaseBitmap, IN PRTL_BITMAP FactorBitmap ); NTSTATUS VspDeleteOldestSnapshot( IN PFILTER_EXTENSION Filter, IN OUT PLIST_ENTRY ListOfDiffAreaFilesToClose, IN OUT PLIST_ENTRY LisfOfDeviceObjectsToDelete, IN BOOLEAN KeepOnDisk, IN BOOLEAN DontWakePnp ); VOID VspCloseDiffAreaFiles( IN PLIST_ENTRY ListOfDiffAreaFilesToClose, IN PLIST_ENTRY ListOfDeviceObjectsToDelete ); NTSTATUS VspComputeIgnorableProduct( IN PVOLUME_EXTENSION Extension ); NTSTATUS VspIoControlItem( IN PFILTER_EXTENSION Filter, IN ULONG ControlItemType, IN GUID* SnapshotGuid, IN BOOLEAN IsSet, IN OUT PVOID ControlItem, IN BOOLEAN AcquireLock ); NTSTATUS VspCleanupControlItemsForSnapshot( IN PVOLUME_EXTENSION Extension ); VOID VspWriteTableUpdates( IN PVSP_DIFF_AREA_FILE DiffAreaFile ); VOID VspKillTableUpdates( IN PVSP_DIFF_AREA_FILE DiffAreaFile ); NTSTATUS VspAllocateDiffAreaSpace( IN PVOLUME_EXTENSION Extension, OUT PLONGLONG TargetOffset, OUT PLONGLONG FileOffset, IN OUT PDIFF_AREA_FILE_ALLOCATION* CurrentFileAllocation, IN OUT PLONGLONG CurrentOffset ); VOID VspResumeVolumeIo( IN PFILTER_EXTENSION Filter ); VOID VspPauseVolumeIo( IN PFILTER_EXTENSION Filter ); NTSTATUS VspCreateDiffAreaFileName( IN PDEVICE_OBJECT DeviceObject, IN PVOLUME_EXTENSION Extension, OUT PUNICODE_STRING DiffAreaFileName, IN BOOLEAN ValidateSystemVolumeInformationFolder, IN GUID* SnapshotGuid ); VOID VspDeleteDiffAreaFilesTimerDpc( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); VOID VspAcquireNonPagedResource( IN PDEVICE_EXTENSION Extension, IN PWORK_QUEUE_ITEM WorkItem, IN BOOLEAN AlwaysPost ); VOID VspReleaseNonPagedResource( IN PDEVICE_EXTENSION Extension ); NTSTATUS VspDeleteControlItemsWithGuid( IN PFILTER_EXTENSION Filter, IN GUID* SnapshotGuid, IN BOOLEAN NonPagedResourceHeld ); NTSTATUS VspCreateStartBlock( IN PFILTER_EXTENSION Filter, IN LONGLONG ControlBlockOffset, IN LONGLONG MaximumDiffAreaSpace ); NTSTATUS VspPinFile( IN PDEVICE_OBJECT TargetObject, IN HANDLE FileHandle ); NTSTATUS VspReadDiffAreaTable( IN PVSP_DIFF_AREA_FILE DiffAreaFile ); PVSP_LOOKUP_TABLE_ENTRY VspFindLookupTableItem( IN PDO_EXTENSION RootExtension, IN GUID* SnapshotGuid ); BOOLEAN VspIsNtfsBootSector( IN PFILTER_EXTENSION Filter, IN PVOID BootSector ); NTSTATUS VspIsNtfs( IN HANDLE FileHandle, OUT PBOOLEAN IsNtfs ); NTSTATUS VspOptimizeDiffAreaFileLocation( IN PFILTER_EXTENSION Filter, IN HANDLE FileHandle, IN PVOLUME_EXTENSION BitmapExtension, IN LONGLONG StartingOffset, IN LONGLONG FileSize ); VOID VspReadSnapshotPhase1( IN PVOID Context ); VOID VspWriteVolumePhase12( IN PVOID TableEntry ); NTSTATUS VspComputeIgnorableBitmap( IN PVOLUME_EXTENSION Extension, IN OUT PRTL_BITMAP Bitmap ); NTSTATUS VspMarkFileAllocationInBitmap( IN PVOLUME_EXTENSION Extension, IN HANDLE FileHandle, IN PVSP_DIFF_AREA_FILE DiffAreaFile, IN PRTL_BITMAP BitmapToSet ); NTSTATUS VolSnapWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID VspStartCopyOnWrite( IN PVOID Context ); VOID VspOnlineWorker( IN PVOID Context ); VOID VspOfflineWorker( IN PVOID Context ); VOID VspStartCopyOnWriteCache( IN PVOID Filter ); VOID VspQueueLowPriorityWorkItem( IN PDO_EXTENSION RootExtension, IN PWORK_QUEUE_ITEM WorkItem ); VOID VspCleanupPreamble( IN PFILTER_EXTENSION Filter ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(INIT, VspIsSetup) #endif #ifdef ALLOC_PRAGMA #pragma code_seg("PAGELK") #endif VOID VspAcquire( IN PDO_EXTENSION RootExtension ) { KeWaitForSingleObject(&RootExtension->Semaphore, Executive, KernelMode, FALSE, NULL); } VOID VspRelease( IN PDO_EXTENSION RootExtension ) { KeReleaseSemaphore(&RootExtension->Semaphore, IO_NO_INCREMENT, 1, FALSE); } VOID VspAcquireCritical( IN PFILTER_EXTENSION Filter ) { KeWaitForSingleObject(&Filter->CriticalOperationSemaphore, Executive, KernelMode, FALSE, NULL); } VOID VspReleaseCritical( IN PFILTER_EXTENSION Filter ) { KeReleaseSemaphore(&Filter->CriticalOperationSemaphore, IO_NO_INCREMENT, 1, FALSE); } PVSP_CONTEXT VspAllocateContext( IN PDO_EXTENSION RootExtension ) { PVSP_CONTEXT context; context = (PVSP_CONTEXT) ExAllocateFromNPagedLookasideList( &RootExtension->ContextLookasideList); return context; } VOID VspFreeContext( IN PDO_EXTENSION RootExtension, IN PVSP_CONTEXT Context ) { KIRQL irql; PLIST_ENTRY l; PIRP irp; PIO_STACK_LOCATION irpSp; if (RootExtension->EmergencyContext == Context) { KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); RootExtension->EmergencyContextInUse = FALSE; if (IsListEmpty(&RootExtension->IrpWaitingList)) { InterlockedExchange(&RootExtension->IrpWaitingListNeedsChecking, FALSE); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } l = RemoveHeadList(&RootExtension->IrpWaitingList); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); irpSp = IoGetCurrentIrpStackLocation(irp); RootExtension->DriverObject->MajorFunction[irpSp->MajorFunction]( irpSp->DeviceObject, irp); return; } ExFreeToNPagedLookasideList(&RootExtension->ContextLookasideList, Context); if (!RootExtension->IrpWaitingListNeedsChecking) { return; } KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); if (IsListEmpty(&RootExtension->IrpWaitingList)) { InterlockedExchange(&RootExtension->IrpWaitingListNeedsChecking, FALSE); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } l = RemoveHeadList(&RootExtension->IrpWaitingList); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); irpSp = IoGetCurrentIrpStackLocation(irp); RootExtension->DriverObject->MajorFunction[irpSp->MajorFunction]( irpSp->DeviceObject, irp); } PVSP_WRITE_CONTEXT VspAllocateWriteContext( IN PDO_EXTENSION RootExtension ) { PVSP_WRITE_CONTEXT writeContext; writeContext = (PVSP_WRITE_CONTEXT) ExAllocateFromNPagedLookasideList( &RootExtension->WriteContextLookasideList); return writeContext; } VOID VspFreeWriteContext( IN PDO_EXTENSION RootExtension, IN PVSP_WRITE_CONTEXT WriteContext ) { KIRQL irql; PLIST_ENTRY l; PIRP irp; PIO_STACK_LOCATION irpSp; if (RootExtension->EmergencyWriteContext == WriteContext) { KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); RootExtension->EmergencyWriteContextInUse = FALSE; if (IsListEmpty(&RootExtension->WriteContextIrpWaitingList)) { InterlockedExchange( &RootExtension->WriteContextIrpWaitingListNeedsChecking, FALSE); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } l = RemoveHeadList(&RootExtension->WriteContextIrpWaitingList); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); irpSp = IoGetCurrentIrpStackLocation(irp); RootExtension->DriverObject->MajorFunction[irpSp->MajorFunction]( irpSp->DeviceObject, irp); return; } ExFreeToNPagedLookasideList(&RootExtension->WriteContextLookasideList, WriteContext); if (!RootExtension->WriteContextIrpWaitingListNeedsChecking) { return; } KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); if (IsListEmpty(&RootExtension->WriteContextIrpWaitingList)) { InterlockedExchange( &RootExtension->WriteContextIrpWaitingListNeedsChecking, FALSE); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } l = RemoveHeadList(&RootExtension->WriteContextIrpWaitingList); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); irpSp = IoGetCurrentIrpStackLocation(irp); RootExtension->DriverObject->MajorFunction[irpSp->MajorFunction]( irpSp->DeviceObject, irp); } PVOID VspAllocateTempTableEntry( IN PDO_EXTENSION RootExtension ) { PVOID tempTableEntry; tempTableEntry = ExAllocateFromNPagedLookasideList( &RootExtension->TempTableEntryLookasideList); return tempTableEntry; } VOID VspQueueWorkItem( IN PDO_EXTENSION RootExtension, IN PWORK_QUEUE_ITEM WorkItem, IN ULONG QueueNumber ) { KIRQL irql; ASSERT(QueueNumber < NUMBER_OF_THREAD_POOLS); KeAcquireSpinLock(&RootExtension->SpinLock[QueueNumber], &irql); InsertTailList(&RootExtension->WorkerQueue[QueueNumber], &WorkItem->List); KeReleaseSpinLock(&RootExtension->SpinLock[QueueNumber], irql); KeReleaseSemaphore(&RootExtension->WorkerSemaphore[QueueNumber], IO_NO_INCREMENT, 1, FALSE); } VOID VspFreeTempTableEntry( IN PDO_EXTENSION RootExtension, IN PVOID TempTableEntry ) { KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; if (RootExtension->EmergencyTableEntry == TempTableEntry) { KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); RootExtension->EmergencyTableEntryInUse = FALSE; if (IsListEmpty(&RootExtension->WorkItemWaitingList)) { InterlockedExchange( &RootExtension->WorkItemWaitingListNeedsChecking, FALSE); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } l = RemoveHeadList(&RootExtension->WorkItemWaitingList); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); VspQueueWorkItem(RootExtension, workItem, 2); return; } ExFreeToNPagedLookasideList(&RootExtension->TempTableEntryLookasideList, TempTableEntry); if (!RootExtension->WorkItemWaitingListNeedsChecking) { return; } KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); if (IsListEmpty(&RootExtension->WorkItemWaitingList)) { InterlockedExchange(&RootExtension->WorkItemWaitingListNeedsChecking, FALSE); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } l = RemoveHeadList(&RootExtension->WorkItemWaitingList); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); VspQueueWorkItem(RootExtension, workItem, 2); } VOID VspLogErrorWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->ErrorLog.Extension; PFILTER_EXTENSION diffAreaFilter = context->ErrorLog.DiffAreaFilter; PFILTER_EXTENSION filter; NTSTATUS status; UNICODE_STRING filterDosName, diffAreaFilterDosName; USHORT systemStringLength, myStringLength, allocSize; USHORT allStringsLimit; USHORT limit; WCHAR buffer[100]; UNICODE_STRING deviceName; PIO_ERROR_LOG_PACKET errorLogPacket; PWCHAR p; ASSERT(context->Type == VSP_CONTEXT_TYPE_ERROR_LOG); if (extension) { filter = extension->Filter; } else { filter = diffAreaFilter; diffAreaFilter = NULL; } status = IoVolumeDeviceToDosName(filter->DeviceObject, &filterDosName); if (!NT_SUCCESS(status)) { goto Cleanup; } myStringLength = filterDosName.Length + sizeof(WCHAR); if (diffAreaFilter) { status = IoVolumeDeviceToDosName(diffAreaFilter->DeviceObject, &diffAreaFilterDosName); if (!NT_SUCCESS(status)) { ExFreePool(filterDosName.Buffer); goto Cleanup; } myStringLength += diffAreaFilterDosName.Length + sizeof(WCHAR); } systemStringLength = 8*sizeof(WCHAR); // Space required for VOLSNAP driver name. if (extension) { swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", extension->VolumeNumber); RtlInitUnicodeString(&deviceName, buffer); systemStringLength += deviceName.Length + sizeof(WCHAR); } else { systemStringLength += sizeof(WCHAR); } limit = ERROR_LOG_MAXIMUM_SIZE - sizeof(IO_ERROR_LOG_PACKET); allStringsLimit = IO_ERROR_LOG_MESSAGE_LENGTH - sizeof(IO_ERROR_LOG_MESSAGE) - ERROR_LOG_ENTRY_SIZE; ASSERT(allStringsLimit > systemStringLength); if (limit > allStringsLimit - systemStringLength) { limit = allStringsLimit - systemStringLength; } if (myStringLength > limit) { if (diffAreaFilter) { limit /= 2; } limit &= ~1; limit -= sizeof(WCHAR); if (filterDosName.Length > limit) { filterDosName.Buffer[3] = '.'; filterDosName.Buffer[4] = '.'; filterDosName.Buffer[5] = '.'; RtlMoveMemory(&filterDosName.Buffer[6], (PCHAR) filterDosName.Buffer + filterDosName.Length - limit + 6*sizeof(WCHAR), limit - 6*sizeof(WCHAR)); filterDosName.Length = limit; filterDosName.Buffer[filterDosName.Length/sizeof(WCHAR)] = 0; } if (diffAreaFilter && diffAreaFilterDosName.Length > limit) { diffAreaFilterDosName.Buffer[3] = '.'; diffAreaFilterDosName.Buffer[4] = '.'; diffAreaFilterDosName.Buffer[5] = '.'; RtlMoveMemory(&diffAreaFilterDosName.Buffer[6], (PCHAR) diffAreaFilterDosName.Buffer + diffAreaFilterDosName.Length - limit + 6*sizeof(WCHAR), limit - 6*sizeof(WCHAR)); diffAreaFilterDosName.Length = limit; diffAreaFilterDosName.Buffer[ diffAreaFilterDosName.Length/sizeof(WCHAR)] = 0; } } errorLogPacket = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(extension ? extension->DeviceObject : filter->DeviceObject, ERROR_LOG_MAXIMUM_SIZE); if (!errorLogPacket) { if (diffAreaFilter) { ExFreePool(diffAreaFilterDosName.Buffer); } ExFreePool(filterDosName.Buffer); goto Cleanup; } errorLogPacket->ErrorCode = context->ErrorLog.SpecificIoStatus; errorLogPacket->SequenceNumber = VsErrorLogSequence++; errorLogPacket->FinalStatus = context->ErrorLog.FinalStatus; errorLogPacket->UniqueErrorValue = context->ErrorLog.UniqueErrorValue; errorLogPacket->DumpDataSize = 0; errorLogPacket->RetryCount = 0; errorLogPacket->NumberOfStrings = 1; errorLogPacket->StringOffset = sizeof(IO_ERROR_LOG_PACKET); p = (PWCHAR) ((PCHAR) errorLogPacket + sizeof(IO_ERROR_LOG_PACKET)); RtlCopyMemory(p, filterDosName.Buffer, filterDosName.Length); p[filterDosName.Length/sizeof(WCHAR)] = 0; if (diffAreaFilter) { errorLogPacket->NumberOfStrings = 2; p = (PWCHAR) ((PCHAR) errorLogPacket + sizeof(IO_ERROR_LOG_PACKET) + filterDosName.Length + sizeof(WCHAR)); RtlCopyMemory(p, diffAreaFilterDosName.Buffer, diffAreaFilterDosName.Length); p[diffAreaFilterDosName.Length/sizeof(WCHAR)] = 0; } IoWriteErrorLogEntry(errorLogPacket); if (diffAreaFilter) { ExFreePool(diffAreaFilterDosName.Buffer); } ExFreePool(filterDosName.Buffer); Cleanup: VspFreeContext(filter->Root, context); if (extension) { ObDereferenceObject(extension->DeviceObject); } ObDereferenceObject(filter->TargetObject); ObDereferenceObject(filter->DeviceObject); if (diffAreaFilter) { ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); } } VOID VspLogError( IN PVOLUME_EXTENSION Extension, IN PFILTER_EXTENSION DiffAreaFilter, IN NTSTATUS SpecificIoStatus, IN NTSTATUS FinalStatus, IN ULONG UniqueErrorValue, IN BOOLEAN PerformSynchronously ) { PDO_EXTENSION root; PVSP_CONTEXT context; if (FinalStatus == STATUS_DEVICE_OFF_LINE) { return; } if (Extension) { root = Extension->Root; } else { root = DiffAreaFilter->Root; } context = VspAllocateContext(root); if (!context) { return; } context->Type = VSP_CONTEXT_TYPE_ERROR_LOG; context->ErrorLog.Extension = Extension; context->ErrorLog.DiffAreaFilter = DiffAreaFilter; context->ErrorLog.SpecificIoStatus = SpecificIoStatus; context->ErrorLog.FinalStatus = FinalStatus; context->ErrorLog.UniqueErrorValue = UniqueErrorValue; if (Extension) { ObReferenceObject(Extension->DeviceObject); ObReferenceObject(Extension->Filter->DeviceObject); ObReferenceObject(Extension->Filter->TargetObject); } if (DiffAreaFilter) { ObReferenceObject(DiffAreaFilter->DeviceObject); ObReferenceObject(DiffAreaFilter->TargetObject); } if (PerformSynchronously) { VspLogErrorWorker(context); return; } ExInitializeWorkItem(&context->WorkItem, VspLogErrorWorker, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } VOID VspWaitForWorkerThreadsToExit( IN PDO_EXTENSION RootExtension ) { PVOID threadObject; CCHAR i, j; if (!RootExtension->WorkerThreadObjects || RootExtension->ThreadsRefCount) { return; } threadObject = RootExtension->WorkerThreadObjects[0]; KeWaitForSingleObject(threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(threadObject); for (i = 1; i < NUMBER_OF_THREAD_POOLS; i++) { for (j = 0; j < KeNumberProcessors; j++) { threadObject = RootExtension->WorkerThreadObjects[ (i - 1)*KeNumberProcessors + j + 1]; KeWaitForSingleObject(threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(threadObject); } } ExFreePool(RootExtension->WorkerThreadObjects); RootExtension->WorkerThreadObjects = NULL; } NTSTATUS VspCreateWorkerThread( IN PDO_EXTENSION RootExtension ) /*++ Routine Description: This routine will create a new thread for a new volume snapshot. Since a minimum of 2 threads are needed to prevent deadlocks, if there are no threads then 2 threads will be created by this routine. Arguments: RootExtension - Supplies the root extension. Return Value: NTSTATUS Notes: The caller must be holding 'Root->Semaphore'. --*/ { OBJECT_ATTRIBUTES oa; PVSP_CONTEXT context; NTSTATUS status; HANDLE handle; PVOID threadObject; CCHAR i, j, k; KeWaitForSingleObject(&RootExtension->ThreadsRefCountSemaphore, Executive, KernelMode, FALSE, NULL); if (RootExtension->ThreadsRefCount) { RootExtension->ThreadsRefCount++; KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } VspWaitForWorkerThreadsToExit(RootExtension); InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); context = VspAllocateContext(RootExtension); if (!context) { KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_THREAD_CREATION; context->ThreadCreation.RootExtension = RootExtension; context->ThreadCreation.QueueNumber = 0; ASSERT(!RootExtension->WorkerThreadObjects); RootExtension->WorkerThreadObjects = (PVOID*) ExAllocatePoolWithTag(NonPagedPool, (KeNumberProcessors*2 + 1)*sizeof(PVOID), VOLSNAP_TAG_IO_STATUS); if (!RootExtension->WorkerThreadObjects) { VspFreeContext(RootExtension, context); KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_INSUFFICIENT_RESOURCES; } status = PsCreateSystemThread(&handle, 0, &oa, 0, NULL, VspWorkerThread, context); if (!NT_SUCCESS(status)) { ExFreePool(RootExtension->WorkerThreadObjects); RootExtension->WorkerThreadObjects = NULL; VspFreeContext(RootExtension, context); KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } status = ObReferenceObjectByHandle(handle, THREAD_ALL_ACCESS, NULL, KernelMode, &threadObject, NULL); if (!NT_SUCCESS(status)) { KeReleaseSemaphore(&RootExtension->WorkerSemaphore[0], IO_NO_INCREMENT, 1, FALSE); ZwWaitForSingleObject(handle, FALSE, NULL); ExFreePool(RootExtension->WorkerThreadObjects); RootExtension->WorkerThreadObjects = NULL; KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } RootExtension->WorkerThreadObjects[0] = threadObject; ZwClose(handle); for (i = 1; i < NUMBER_OF_THREAD_POOLS; i++) { for (j = 0; j < KeNumberProcessors; j++) { context = VspAllocateContext(RootExtension); if (!context) { status = STATUS_INSUFFICIENT_RESOURCES; handle = NULL; break; } context->Type = VSP_CONTEXT_TYPE_THREAD_CREATION; context->ThreadCreation.RootExtension = RootExtension; context->ThreadCreation.QueueNumber = i; status = PsCreateSystemThread(&handle, 0, &oa, 0, NULL, VspWorkerThread, context); if (!NT_SUCCESS(status)) { VspFreeContext(RootExtension, context); handle = NULL; break; } status = ObReferenceObjectByHandle( handle, THREAD_ALL_ACCESS, NULL, KernelMode, &threadObject, NULL); if (!NT_SUCCESS(status)) { break; } RootExtension->WorkerThreadObjects[ KeNumberProcessors*(i - 1) + j + 1] = threadObject; ZwClose(handle); } if (j < KeNumberProcessors) { KeReleaseSemaphore(&RootExtension->WorkerSemaphore[i], IO_NO_INCREMENT, j, FALSE); if (handle) { KeReleaseSemaphore(&RootExtension->WorkerSemaphore[i], IO_NO_INCREMENT, 1, FALSE); ZwWaitForSingleObject(handle, FALSE, NULL); ZwClose(handle); } for (k = 0; k < j; k++) { threadObject = RootExtension->WorkerThreadObjects[ KeNumberProcessors*(i - 1) + k + 1]; KeWaitForSingleObject(threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(threadObject); } break; } } if (i < NUMBER_OF_THREAD_POOLS) { for (k = 1; k < i; k++) { KeReleaseSemaphore(&RootExtension->WorkerSemaphore[k], IO_NO_INCREMENT, KeNumberProcessors, FALSE); for (j = 0; j < KeNumberProcessors; j++) { threadObject = RootExtension->WorkerThreadObjects[ KeNumberProcessors*(k - 1) + j + 1]; KeWaitForSingleObject(threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(threadObject); } } KeReleaseSemaphore(&RootExtension->WorkerSemaphore[0], IO_NO_INCREMENT, 1, FALSE); threadObject = RootExtension->WorkerThreadObjects[0]; KeWaitForSingleObject(threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(threadObject); ExFreePool(RootExtension->WorkerThreadObjects); RootExtension->WorkerThreadObjects = NULL; KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } RootExtension->ThreadsRefCount++; KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } VOID VspWaitForWorkerThreadsToExitWorker( IN PVOID RootExtension ) { PDO_EXTENSION rootExtension = (PDO_EXTENSION) RootExtension; KeWaitForSingleObject(&rootExtension->ThreadsRefCountSemaphore, Executive, KernelMode, FALSE, NULL); VspWaitForWorkerThreadsToExit(rootExtension); rootExtension->WaitForWorkerThreadsToExitWorkItemInUse = FALSE; KeReleaseSemaphore(&rootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); } NTSTATUS VspDeleteWorkerThread( IN PDO_EXTENSION RootExtension ) /*++ Routine Description: This routine will delete a worker thread. Arguments: RootExtension - Supplies the root extension. Return Value: NTSTATUS Notes: The caller must be holding 'Root->Semaphore'. --*/ { CCHAR i, j; KeWaitForSingleObject(&RootExtension->ThreadsRefCountSemaphore, Executive, KernelMode, FALSE, NULL); RootExtension->ThreadsRefCount--; if (RootExtension->ThreadsRefCount) { KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } KeReleaseSemaphore(&RootExtension->WorkerSemaphore[0], IO_NO_INCREMENT, 1, FALSE); for (i = 1; i < NUMBER_OF_THREAD_POOLS; i++) { KeReleaseSemaphore(&RootExtension->WorkerSemaphore[i], IO_NO_INCREMENT, KeNumberProcessors, FALSE); } if (RootExtension->WaitForWorkerThreadsToExitWorkItemInUse) { KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } RootExtension->WaitForWorkerThreadsToExitWorkItemInUse = TRUE; ExInitializeWorkItem(&RootExtension->WaitForWorkerThreadsToExitWorkItem, VspWaitForWorkerThreadsToExitWorker, RootExtension); ExQueueWorkItem(&RootExtension->WaitForWorkerThreadsToExitWorkItem, DelayedWorkQueue); KeReleaseSemaphore(&RootExtension->ThreadsRefCountSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } VOID VspQueryDiffAreaFileIncrease( IN PVOLUME_EXTENSION Extension, OUT PULONG Increase ) { LONGLONG r; r = (LONGLONG) Extension->MaximumNumberOfTempEntries; r <<= BLOCK_SHIFT; r = (r + LARGEST_NTFS_CLUSTER - 1)&(~(LARGEST_NTFS_CLUSTER - 1)); if (r < NOMINAL_DIFF_AREA_FILE_GROWTH) { r = NOMINAL_DIFF_AREA_FILE_GROWTH; } else if (r > MAXIMUM_DIFF_AREA_FILE_GROWTH) { r = MAXIMUM_DIFF_AREA_FILE_GROWTH; } *Increase = (ULONG) r; } NTSTATUS VolSnapDefaultDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the default dispatch which passes down to the next layer. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); if (irpSp->MajorFunction == IRP_MJ_SYSTEM_CONTROL) { status = Irp->IoStatus.Status; } else { status = STATUS_INVALID_DEVICE_REQUEST; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } BOOLEAN VspAreBitsClear( IN PRTL_BITMAP Bitmap, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); LONGLONG start; ULONG startBlock, endBlock; BOOLEAN b; start = irpSp->Parameters.Read.ByteOffset.QuadPart; if (start < 0) { return FALSE; } startBlock = (ULONG) (start >> BLOCK_SHIFT); endBlock = (ULONG) ((start + irpSp->Parameters.Read.Length - 1) >> BLOCK_SHIFT); b = RtlAreBitsClear(Bitmap, startBlock, endBlock - startBlock + 1); return b; } BOOLEAN VspAreBitsSet( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); LONGLONG start; ULONG startBlock, endBlock; BOOLEAN b; if (!Extension->VolumeBlockBitmap) { return FALSE; } start = irpSp->Parameters.Read.ByteOffset.QuadPart; if (start < 0) { return FALSE; } startBlock = (ULONG) (start >> BLOCK_SHIFT); endBlock = (ULONG) ((start + irpSp->Parameters.Read.Length - 1) >> BLOCK_SHIFT); b = RtlAreBitsSet(Extension->VolumeBlockBitmap, startBlock, endBlock - startBlock + 1); return b; } VOID VspDecrementVolumeRefCount( IN PVOLUME_EXTENSION Extension ) { if (InterlockedDecrement(&Extension->RefCount)) { return; } ASSERT(Extension->HoldIncomingRequests); KeSetEvent(&Extension->ZeroRefEvent, IO_NO_INCREMENT, FALSE); } NTSTATUS VspIncrementVolumeRefCount( IN PVOLUME_EXTENSION Extension ) { InterlockedIncrement(&Extension->RefCount); if (Extension->IsDead) { VspDecrementVolumeRefCount(Extension); return STATUS_NO_SUCH_DEVICE; } ASSERT(!Extension->HoldIncomingRequests); return STATUS_SUCCESS; } VOID VspSignalContext( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; ASSERT(context->Type == VSP_CONTEXT_TYPE_EVENT); KeSetEvent(&context->Event.Event, IO_NO_INCREMENT, FALSE); } VOID VspAcquirePagedResource( IN PVOLUME_EXTENSION Extension, IN PWORK_QUEUE_ITEM WorkItem ) { PFILTER_EXTENSION filter = Extension->Filter; KIRQL irql; VSP_CONTEXT context; BOOLEAN synchronousCall; if (WorkItem) { synchronousCall = FALSE; } else { WorkItem = &context.WorkItem; context.Type = VSP_CONTEXT_TYPE_EVENT; KeInitializeEvent(&context.Event.Event, NotificationEvent, FALSE); ExInitializeWorkItem(&context.WorkItem, VspSignalContext, &context); synchronousCall = TRUE; } KeAcquireSpinLock(&filter->SpinLock, &irql); if (filter->PagedResourceInUse) { InsertTailList(&filter->PagedResourceList, &WorkItem->List); KeReleaseSpinLock(&filter->SpinLock, irql); if (synchronousCall) { KeWaitForSingleObject(&context.Event.Event, Executive, KernelMode, FALSE, NULL); } return; } filter->PagedResourceInUse = TRUE; KeReleaseSpinLock(&filter->SpinLock, irql); if (!synchronousCall) { VspQueueWorkItem(filter->Root, WorkItem, 1); } } VOID VspReleasePagedResource( IN PVOLUME_EXTENSION Extension ) { PFILTER_EXTENSION filter = Extension->Filter; KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; KeAcquireSpinLock(&filter->SpinLock, &irql); if (IsListEmpty(&filter->PagedResourceList)) { filter->PagedResourceInUse = FALSE; KeReleaseSpinLock(&filter->SpinLock, irql); return; } l = RemoveHeadList(&filter->PagedResourceList); KeReleaseSpinLock(&filter->SpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); if (workItem->WorkerRoutine == VspSignalContext) { workItem->WorkerRoutine(workItem->Parameter); } else { VspQueueWorkItem(Extension->Root, workItem, 1); } } NTSTATUS VspQueryListOfExtents( IN HANDLE FileHandle, IN LONGLONG FileOffset, OUT PLIST_ENTRY ExtentList, IN PVOLUME_EXTENSION BitmapExtension, IN BOOLEAN ReturnRawValues ) { NTSTATUS status; IO_STATUS_BLOCK ioStatus; FILE_FS_SIZE_INFORMATION fsSize; ULONG bpc; STARTING_VCN_INPUT_BUFFER input; RETRIEVAL_POINTERS_BUFFER output; LONGLONG start, length, delta, end, roundedStart, roundedEnd, s; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PLIST_ENTRY l; KIRQL irql; BOOLEAN isNegative, isNtfs; InitializeListHead(ExtentList); status = ZwQueryVolumeInformationFile(FileHandle, &ioStatus, &fsSize, sizeof(fsSize), FileFsSizeInformation); if (!NT_SUCCESS(status)) { while (!IsListEmpty(ExtentList)) { l = RemoveHeadList(ExtentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } return status; } bpc = fsSize.BytesPerSector*fsSize.SectorsPerAllocationUnit; input.StartingVcn.QuadPart = FileOffset/bpc; for (;;) { status = ZwFsControlFile(FileHandle, NULL, NULL, NULL, &ioStatus, FSCTL_GET_RETRIEVAL_POINTERS, &input, sizeof(input), &output, sizeof(output)); if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) { if (status == STATUS_END_OF_FILE) { status = STATUS_SUCCESS; } break; } if (!output.ExtentCount) { status = STATUS_INVALID_PARAMETER; break; } if (output.Extents[0].Lcn.QuadPart == -1) { if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; continue; } delta = input.StartingVcn.QuadPart - output.StartingVcn.QuadPart; start = (output.Extents[0].Lcn.QuadPart + delta)*bpc; length = (output.Extents[0].NextVcn.QuadPart - input.StartingVcn.QuadPart)*bpc; end = start + length; if (ReturnRawValues) { diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = start; diffAreaFileAllocation->NLength = length; InsertTailList(ExtentList, &diffAreaFileAllocation->ListEntry); if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; continue; } roundedStart = start&(~(BLOCK_SIZE - 1)); roundedEnd = end&(~(BLOCK_SIZE - 1)); if (start != roundedStart) { roundedStart += BLOCK_SIZE; } if (roundedStart > start) { diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = start; diffAreaFileAllocation->NLength = -(roundedStart - start); if (roundedStart > end) { diffAreaFileAllocation->NLength += roundedStart - end; } ASSERT(diffAreaFileAllocation->NLength); InsertTailList(ExtentList, &diffAreaFileAllocation->ListEntry); } diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = roundedStart; diffAreaFileAllocation->NLength = 0; for (s = roundedStart; s < roundedEnd; s += BLOCK_SIZE) { isNegative = FALSE; if (BitmapExtension) { KeAcquireSpinLock(&BitmapExtension->SpinLock, &irql); if (BitmapExtension->VolumeBlockBitmap && !RtlCheckBit(BitmapExtension->VolumeBlockBitmap, s>>BLOCK_SHIFT)) { isNegative = TRUE; } KeReleaseSpinLock(&BitmapExtension->SpinLock, irql); } if (isNegative) { if (diffAreaFileAllocation->NLength <= 0) { diffAreaFileAllocation->NLength -= BLOCK_SIZE; } else { InsertTailList(ExtentList, &diffAreaFileAllocation->ListEntry); diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = s; diffAreaFileAllocation->NLength = -BLOCK_SIZE; } } else { if (diffAreaFileAllocation->NLength >= 0) { diffAreaFileAllocation->NLength += BLOCK_SIZE; } else { InsertTailList(ExtentList, &diffAreaFileAllocation->ListEntry); diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = s; diffAreaFileAllocation->NLength = BLOCK_SIZE; } } } if (s < roundedEnd) { break; } if (diffAreaFileAllocation->NLength) { InsertTailList(ExtentList, &diffAreaFileAllocation->ListEntry); } else { ExFreePool(diffAreaFileAllocation); } if (end > roundedEnd && roundedEnd >= roundedStart) { diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = roundedEnd; diffAreaFileAllocation->NLength = -(end - roundedEnd); InsertTailList(ExtentList, &diffAreaFileAllocation->ListEntry); } if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; } if (!NT_SUCCESS(status)) { while (!IsListEmpty(ExtentList)) { l = RemoveHeadList(ExtentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } } return status; } NTSTATUS VspQueryFileSize( IN HANDLE FileHandle, IN PLONGLONG FileSize ) { FILE_STANDARD_INFORMATION allocInfo; NTSTATUS status; IO_STATUS_BLOCK ioStatus; status = ZwQueryInformationFile(FileHandle, &ioStatus, &allocInfo, sizeof(allocInfo), FileStandardInformation); *FileSize = allocInfo.AllocationSize.QuadPart; return status; } NTSTATUS VspSetFileSize( IN HANDLE FileHandle, IN LONGLONG FileSize ) { FILE_ALLOCATION_INFORMATION allocInfo; FILE_END_OF_FILE_INFORMATION eofInfo; NTSTATUS status; IO_STATUS_BLOCK ioStatus; allocInfo.AllocationSize.QuadPart = FileSize; eofInfo.EndOfFile.QuadPart = FileSize; status = ZwSetInformationFile(FileHandle, &ioStatus, &eofInfo, sizeof(eofInfo), FileEndOfFileInformation); if (!NT_SUCCESS(status)) { return status; } status = ZwSetInformationFile(FileHandle, &ioStatus, &allocInfo, sizeof(allocInfo), FileAllocationInformation); return status; } NTSTATUS VspSynchronousIo( IN PIRP Irp, IN PDEVICE_OBJECT TargetObject, IN UCHAR MajorFunction, IN LONGLONG Offset, IN ULONG Length ) { PIO_STACK_LOCATION nextSp; KEVENT event; if (!Length) { Length = BLOCK_SIZE; } nextSp = IoGetNextIrpStackLocation(Irp); Irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Read.ByteOffset.QuadPart = Offset; nextSp->Parameters.Read.Length = Length; nextSp->MajorFunction = MajorFunction; nextSp->DeviceObject = TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; KeInitializeEvent(&event, NotificationEvent, FALSE); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); return Irp->IoStatus.Status; } VOID VspFreeUsedDiffAreaSpaceFromPointers( IN PVOLUME_EXTENSION Extension, IN PDIFF_AREA_FILE_ALLOCATION CurrentFileAllocation, IN LONGLONG CurrentOffset ) { PVSP_DIFF_AREA_FILE diffAreaFile = Extension->DiffAreaFile; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; ASSERT(diffAreaFile); for (;;) { if (IsListEmpty(&diffAreaFile->UnusedAllocationList)) { return; } l = diffAreaFile->UnusedAllocationList.Flink; diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation == CurrentFileAllocation) { break; } RemoveEntryList(l); ExFreePool(diffAreaFileAllocation); } ASSERT(diffAreaFileAllocation->NLength >= 0 || !CurrentOffset); diffAreaFileAllocation->Offset += CurrentOffset; diffAreaFileAllocation->NLength -= CurrentOffset; ASSERT(diffAreaFileAllocation->NLength >= 0 || !CurrentOffset); } NTSTATUS VspAddLocationDescription( IN PVSP_DIFF_AREA_FILE DiffAreaFile, IN LONGLONG OldAllocatedFileSize ) { PVOLUME_EXTENSION extension = DiffAreaFile->Extension; LONGLONG fileOffset, t, f; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PVSP_BLOCK_DIFF_AREA_LOCATION_DESCRIPTION locationBlock; PVSP_DIFF_AREA_LOCATION_DESCRIPTOR locationDescriptor; ULONG blockOffset; ULONG totalNumEntries, numEntriesPerBlock, numBlocks, i; PLONGLONG fileOffsetArray, targetOffsetArray; NTSTATUS status; PDIFF_AREA_FILE_ALLOCATION allocationBlock; LONGLONG allocationOffset; fileOffset = DiffAreaFile->NextAvailable; for (l = DiffAreaFile->UnusedAllocationList.Flink; l != &DiffAreaFile->UnusedAllocationList; l = l->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (fileOffset == OldAllocatedFileSize) { break; } if (diffAreaFileAllocation->NLength < 0) { fileOffset -= diffAreaFileAllocation->NLength; } else { fileOffset += diffAreaFileAllocation->NLength; } } ASSERT(l != &DiffAreaFile->UnusedAllocationList); locationBlock = (PVSP_BLOCK_DIFF_AREA_LOCATION_DESCRIPTION) MmGetMdlVirtualAddress(DiffAreaFile->TableUpdateIrp->MdlAddress); allocationOffset = 0; allocationBlock = CONTAINING_RECORD( DiffAreaFile->UnusedAllocationList.Flink, DIFF_AREA_FILE_ALLOCATION, ListEntry); for (;;) { status = VspSynchronousIo( DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, DiffAreaFile->DiffAreaLocationDescriptionTargetOffset, 0); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 1, FALSE); } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); return status; } for (blockOffset = VSP_OFFSET_TO_FIRST_LOCATION_DESCRIPTOR; blockOffset + sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR) <= BLOCK_SIZE; blockOffset += sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR)) { locationDescriptor = (PVSP_DIFF_AREA_LOCATION_DESCRIPTOR) ((PCHAR) locationBlock + blockOffset); if (locationDescriptor->VolumeOffset) { continue; } while (diffAreaFileAllocation->NLength <= 0) { fileOffset -= diffAreaFileAllocation->NLength; l = l->Flink; if (l == &DiffAreaFile->UnusedAllocationList) { break; } diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); } if (l == &DiffAreaFile->UnusedAllocationList) { break; } locationDescriptor->VolumeOffset = diffAreaFileAllocation->Offset; locationDescriptor->FileOffset = fileOffset; locationDescriptor->Length = diffAreaFileAllocation->NLength; fileOffset += locationDescriptor->Length; l = l->Flink; if (l == &DiffAreaFile->UnusedAllocationList) { break; } diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); } status = VspSynchronousIo( DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, DiffAreaFile->DiffAreaLocationDescriptionTargetOffset, 0); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 22, FALSE); } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); return status; } if (l == &DiffAreaFile->UnusedAllocationList) { break; } status = VspAllocateDiffAreaSpace(extension, &t, &f, &allocationBlock, &allocationOffset); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_OUT_OF_DIFF_AREA, STATUS_SUCCESS, 5, FALSE); } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); return status; } RtlZeroMemory(locationBlock, BLOCK_SIZE); locationBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; locationBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; locationBlock->Header.BlockType = VSP_BLOCK_TYPE_DIFF_AREA_LOCATION_DESCRIPTION; locationBlock->Header.ThisFileOffset = f; locationBlock->Header.ThisVolumeOffset = t; status = VspSynchronousIo( DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, t, 0); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 23, FALSE); } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); return status; } status = VspSynchronousIo( DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, DiffAreaFile->DiffAreaLocationDescriptionTargetOffset, 0); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 24, FALSE); } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); return status; } locationBlock->Header.NextVolumeOffset = t; status = VspSynchronousIo( DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, DiffAreaFile->DiffAreaLocationDescriptionTargetOffset, 0); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 25, FALSE); } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); return status; } DiffAreaFile->DiffAreaLocationDescriptionTargetOffset = t; } VspFreeUsedDiffAreaSpaceFromPointers(extension, allocationBlock, allocationOffset); status = VspSynchronousIo( DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, DiffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 26, FALSE); } return status; } return STATUS_SUCCESS; } VOID VspReleaseDiffAreaSpaceWaiters( IN PVOLUME_EXTENSION Extension ) { KIRQL irql; BOOLEAN emptyQueue; LIST_ENTRY q; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->GrowDiffAreaFilePending) { Extension->GrowDiffAreaFilePending = FALSE; if (IsListEmpty(&Extension->WaitingForDiffAreaSpace)) { emptyQueue = FALSE; } else { emptyQueue = TRUE; q = Extension->WaitingForDiffAreaSpace; InitializeListHead(&Extension->WaitingForDiffAreaSpace); } } else { emptyQueue = FALSE; } KeReleaseSpinLock(&Extension->SpinLock, irql); if (emptyQueue) { q.Flink->Blink = &q; q.Blink->Flink = &q; while (!IsListEmpty(&q)) { l = RemoveHeadList(&q); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); VspAcquireNonPagedResource(Extension, workItem, FALSE); } } } VOID VspGrowDiffAreaPhase2( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->GrowDiffArea.Extension; PVSP_DIFF_AREA_FILE diffAreaFile = extension->DiffAreaFile; PFILTER_EXTENSION filter = extension->Filter; PLIST_ENTRY extentList = &context->GrowDiffArea.ExtentList; LONGLONG current = context->GrowDiffArea.Current; ULONG increase = context->GrowDiffArea.Increase; NTSTATUS status = context->GrowDiffArea.ResultStatus; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; KIRQL irql; BOOLEAN dontNeedWrite; ASSERT(context->Type == VSP_CONTEXT_TYPE_GROW_DIFF_AREA); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(extension); VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); VspDecrementVolumeRefCount(extension); return; } while (!IsListEmpty(extentList)) { l = RemoveHeadList(extentList); InsertTailList(&diffAreaFile->UnusedAllocationList, l); } diffAreaFile->AllocatedFileSize += increase; KeAcquireSpinLock(&filter->SpinLock, &irql); filter->AllocatedVolumeSpace += increase; KeReleaseSpinLock(&filter->SpinLock, irql); if (extension->IsPersistent && diffAreaFile->TableUpdateIrp) { KeAcquireSpinLock(&extension->SpinLock, &irql); dontNeedWrite = diffAreaFile->TableUpdateInProgress; diffAreaFile->TableUpdateInProgress = TRUE; if (dontNeedWrite) { KeInitializeEvent(&diffAreaFile->IrpReady, NotificationEvent, FALSE); ASSERT(!diffAreaFile->IrpNeeded); diffAreaFile->IrpNeeded = TRUE; } KeReleaseSpinLock(&extension->SpinLock, irql); if (dontNeedWrite) { KeWaitForSingleObject(&diffAreaFile->IrpReady, Executive, KernelMode, FALSE, NULL); } status = VspAddLocationDescription(diffAreaFile, current); if (NT_SUCCESS(status)) { VspWriteTableUpdates(diffAreaFile); } else { VspKillTableUpdates(diffAreaFile); } } VspReleaseNonPagedResource(extension); VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); VspDecrementVolumeRefCount(extension); } NTSTATUS VspDiffAreaFileFillCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; LONGLONG offset = 0; KIRQL irql; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaAllocation; PIO_STACK_LOCATION nextSp; ASSERT(context->Type == VSP_CONTEXT_TYPE_GROW_DIFF_AREA); if (!NT_SUCCESS(Irp->IoStatus.Status)) { KeAcquireSpinLock(&context->GrowDiffArea.SpinLock, &irql); context->GrowDiffArea.CurrentEntry = &context->GrowDiffArea.ExtentList; context->GrowDiffArea.CurrentEntryOffset = 0; KeReleaseSpinLock(&context->GrowDiffArea.SpinLock, irql); context->GrowDiffArea.ResultStatus = Irp->IoStatus.Status; } KeAcquireSpinLock(&context->GrowDiffArea.SpinLock, &irql); for (l = context->GrowDiffArea.CurrentEntry; l != &context->GrowDiffArea.ExtentList; l = l->Flink) { diffAreaAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaAllocation->NLength <= 0) { ASSERT(!context->GrowDiffArea.CurrentEntryOffset); continue; } if (context->GrowDiffArea.CurrentEntryOffset == diffAreaAllocation->NLength) { context->GrowDiffArea.CurrentEntryOffset = 0; continue; } ASSERT(context->GrowDiffArea.CurrentEntryOffset < diffAreaAllocation->NLength); offset = diffAreaAllocation->Offset + context->GrowDiffArea.CurrentEntryOffset; context->GrowDiffArea.CurrentEntryOffset += BLOCK_SIZE; break; } context->GrowDiffArea.CurrentEntry = l; KeReleaseSpinLock(&context->GrowDiffArea.SpinLock, irql); if (l == &context->GrowDiffArea.ExtentList) { if (!InterlockedDecrement(&context->GrowDiffArea.RefCount)) { ExFreePool(MmGetMdlVirtualAddress(Irp->MdlAddress)); IoFreeMdl(Irp->MdlAddress); VspAcquireNonPagedResource(context->GrowDiffArea.Extension, &context->WorkItem, TRUE); } IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } nextSp = IoGetNextIrpStackLocation(Irp); Irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = offset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = context->GrowDiffArea.TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(Irp, VspDiffAreaFileFillCompletion, context, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, Irp); return STATUS_MORE_PROCESSING_REQUIRED; } VOID VspLaunchDiffAreaFill( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; ULONG i; PVOID buffer; PMDL mdl; PIRP irp; PIO_STACK_LOCATION nextSp; ASSERT(context->Type == VSP_CONTEXT_TYPE_GROW_DIFF_AREA); context->GrowDiffArea.ResultStatus = STATUS_SUCCESS; buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { context->GrowDiffArea.ResultStatus = STATUS_INSUFFICIENT_RESOURCES; VspAcquireNonPagedResource(context->GrowDiffArea.Extension, &context->WorkItem, TRUE); return; } mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buffer); context->GrowDiffArea.ResultStatus = STATUS_INSUFFICIENT_RESOURCES; VspAcquireNonPagedResource(context->GrowDiffArea.Extension, &context->WorkItem, TRUE); return; } MmBuildMdlForNonPagedPool(mdl); ASSERT(SMALLEST_NTFS_CLUSTER < BLOCK_SIZE); RtlZeroMemory(buffer, BLOCK_SIZE); for (i = 0; i < BLOCK_SIZE; i += SMALLEST_NTFS_CLUSTER) { RtlCopyMemory((PCHAR) buffer + i, &VSP_DIFF_AREA_FILE_GUID, sizeof(GUID)); } context->GrowDiffArea.RefCount = 1; for (i = 0; i < 32; i++) { irp = IoAllocateIrp( (CCHAR) context->GrowDiffArea.Extension->Root->StackSize, FALSE); if (!irp) { if (!i) { context->GrowDiffArea.ResultStatus = STATUS_INSUFFICIENT_RESOURCES; } break; } irp->MdlAddress = mdl; irp->IoStatus.Status = STATUS_SUCCESS; InterlockedIncrement(&context->GrowDiffArea.RefCount); VspDiffAreaFileFillCompletion(NULL, irp, context); } if (!InterlockedDecrement(&context->GrowDiffArea.RefCount)) { ExFreePool(buffer); IoFreeMdl(mdl); VspAcquireNonPagedResource(context->GrowDiffArea.Extension, &context->WorkItem, TRUE); } } VOID VspGrowDiffArea( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->GrowDiffArea.Extension; PVSP_DIFF_AREA_FILE diffAreaFile = extension->DiffAreaFile; PFILTER_EXTENSION filter = extension->Filter; LONGLONG usableSpace = 0; PFILTER_EXTENSION diffFilter; HANDLE handle, h; NTSTATUS status, status2; KIRQL irql; LIST_ENTRY extentList; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; ULONG increase, increaseDelta; LONGLONG current; PVOLUME_EXTENSION bitmapExtension; LIST_ENTRY listOfDiffAreaFilesToClose; LIST_ENTRY listOfDeviceObjectsToDelete; BOOLEAN reduceIncreaseOk; PVOLUME_EXTENSION e; KPRIORITY oldPriority; ASSERT(context->Type == VSP_CONTEXT_TYPE_GROW_DIFF_AREA); status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); ObDereferenceObject(extension->DeviceObject); return; } ASSERT(diffAreaFile); current = diffAreaFile->AllocatedFileSize; increaseDelta = extension->DiffAreaFileIncrease; increase = 2*increaseDelta; diffFilter = diffAreaFile->Filter; handle = diffAreaFile->FileHandle; reduceIncreaseOk = TRUE; for (;;) { KeAcquireSpinLock(&diffFilter->SpinLock, &irql); if (filter->MaximumVolumeSpace && filter->AllocatedVolumeSpace + increase > filter->MaximumVolumeSpace) { KeReleaseSpinLock(&diffFilter->SpinLock, irql); status = STATUS_DISK_FULL; extension->UserImposedLimit = TRUE; } else { KeReleaseSpinLock(&diffFilter->SpinLock, irql); status = ZwDuplicateObject(NtCurrentProcess(), handle, NtCurrentProcess(), &h, 0, 0, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES); if (NT_SUCCESS(status)) { ObReferenceObject(diffFilter->DeviceObject); InterlockedIncrement(&diffFilter->FSRefCount); VspDecrementVolumeRefCount(extension); status = VspSetFileSize(h, current + increase); ZwClose(h); InterlockedDecrement(&diffFilter->FSRefCount); ObDereferenceObject(diffFilter->DeviceObject); status2 = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status2)) { VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); ObDereferenceObject(extension->DeviceObject); return; } } if (NT_SUCCESS(status)) { break; } extension->UserImposedLimit = FALSE; } if (increaseDelta > NOMINAL_DIFF_AREA_FILE_GROWTH && reduceIncreaseOk) { increase = 2*NOMINAL_DIFF_AREA_FILE_GROWTH; increaseDelta = NOMINAL_DIFF_AREA_FILE_GROWTH; reduceIncreaseOk = FALSE; continue; } VspDecrementVolumeRefCount(extension); VspReleaseDiffAreaSpaceWaiters(extension); VspAcquire(extension->Root); if (extension->IsDead) { InterlockedExchange(&extension->GrowFailed, TRUE); VspRelease(extension->Root); VspFreeContext(extension->Root, context); ObDereferenceObject(extension->DeviceObject); return; } if (status != STATUS_DISK_FULL) { InterlockedExchange(&extension->GrowFailed, TRUE); VspLogError(extension, diffFilter, VS_GROW_DIFF_AREA_FAILED, status, 1, FALSE); VspRelease(extension->Root); VspFreeContext(extension->Root, context); ObDereferenceObject(extension->DeviceObject); return; } if (extension->ListEntry.Blink == &filter->VolumeList) { InterlockedExchange(&extension->GrowFailed, TRUE); if (extension->UserImposedLimit) { VspLogError(extension, diffFilter, VS_GROW_DIFF_AREA_FAILED_LOW_DISK_SPACE_USER_IMPOSED, STATUS_DISK_FULL, 0, FALSE); } else { VspLogError(extension, diffFilter, VS_GROW_DIFF_AREA_FAILED_LOW_DISK_SPACE, STATUS_DISK_FULL, 0, FALSE); } VspRelease(extension->Root); VspFreeContext(extension->Root, context); ObDereferenceObject(extension->DeviceObject); return; } e = CONTAINING_RECORD(filter->VolumeList.Flink, VOLUME_EXTENSION, ListEntry); VspLogError(e, NULL, VS_DELETE_TO_TRIM_SPACE, STATUS_SUCCESS, 1, TRUE); InitializeListHead(&listOfDiffAreaFilesToClose); InitializeListHead(&listOfDeviceObjectsToDelete); VspDeleteOldestSnapshot(filter, &listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete, FALSE, FALSE); VspRelease(extension->Root); VspCloseDiffAreaFiles(&listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete); status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { VspFreeContext(extension->Root, context); ObDereferenceObject(extension->DeviceObject); return; } } VspDecrementVolumeRefCount(extension); if (extension->IsDead) { VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); ObDereferenceObject(extension->DeviceObject); return; } KeAcquireSpinLock(&diffFilter->SpinLock, &irql); if (IsListEmpty(&diffFilter->VolumeList)) { bitmapExtension = NULL; } else { bitmapExtension = CONTAINING_RECORD(diffFilter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); if (bitmapExtension->IsDead) { bitmapExtension = NULL; } else { ObReferenceObject(bitmapExtension->DeviceObject); } } KeReleaseSpinLock(&diffFilter->SpinLock, irql); status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); ObDereferenceObject(extension->DeviceObject); return; } status = VspQueryListOfExtents(handle, current, &extentList, bitmapExtension, FALSE); if (!NT_SUCCESS(status)) { if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); VspDecrementVolumeRefCount(extension); ObDereferenceObject(extension->DeviceObject); return; } usableSpace = 0; for (l = extentList.Flink; l != &extentList; l = l->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength > 0) { usableSpace += diffAreaFileAllocation->NLength; } } if (usableSpace < increaseDelta) { while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } oldPriority = KeQueryPriorityThread(KeGetCurrentThread()); KeSetPriorityThread(KeGetCurrentThread(), VSP_LOWER_PRIORITY); VspOptimizeDiffAreaFileLocation(diffFilter, handle, bitmapExtension, current, current + increase); status = VspQueryListOfExtents(handle, current, &extentList, bitmapExtension, FALSE); KeSetPriorityThread(KeGetCurrentThread(), oldPriority); if (!NT_SUCCESS(status)) { if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); VspDecrementVolumeRefCount(extension); ObDereferenceObject(extension->DeviceObject); return; } usableSpace = 0; for (l = extentList.Flink; l != &extentList; l = l->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength > 0) { usableSpace += diffAreaFileAllocation->NLength; } } if (!usableSpace) { if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); VspDecrementVolumeRefCount(extension); ObDereferenceObject(extension->DeviceObject); return; } } if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } ObDereferenceObject(extension->DeviceObject); KeAcquireSpinLock(&extension->SpinLock, &irql); extension->PastFileSystemOperations = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); ExInitializeWorkItem(&context->WorkItem, VspGrowDiffAreaPhase2, context); ASSERT(!IsListEmpty(&extentList)); context->GrowDiffArea.ExtentList = extentList; context->GrowDiffArea.ExtentList.Flink->Blink = &context->GrowDiffArea.ExtentList; context->GrowDiffArea.ExtentList.Blink->Flink = &context->GrowDiffArea.ExtentList; context->GrowDiffArea.Current = current; context->GrowDiffArea.Increase = increase; context->GrowDiffArea.ResultStatus = STATUS_SUCCESS; if (extension->IsPersistent && !extension->NoDiffAreaFill) { KeInitializeSpinLock(&context->GrowDiffArea.SpinLock); context->GrowDiffArea.CurrentEntry = context->GrowDiffArea.ExtentList.Flink; context->GrowDiffArea.CurrentEntryOffset = 0; context->GrowDiffArea.TargetObject = diffFilter->TargetObject; VspLaunchDiffAreaFill(context); return; } VspAcquireNonPagedResource(extension, &context->WorkItem, FALSE); } VOID VspWaitForInstall( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->GrowDiffArea.Extension; PFILTER_EXTENSION filter = extension->Filter; NTSTATUS status; ASSERT(context->Type == VSP_CONTEXT_TYPE_GROW_DIFF_AREA); KeWaitForSingleObject(&filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); ObDereferenceObject(extension->DeviceObject); ObDereferenceObject(filter->DeviceObject); return; } if (!extension->OkToGrowDiffArea) { VspLogError(extension, extension->DiffAreaFile->Filter, VS_GROW_BEFORE_FREE_SPACE, STATUS_SUCCESS, 1, FALSE); VspDecrementVolumeRefCount(extension); VspFreeContext(extension->Root, context); VspReleaseDiffAreaSpaceWaiters(extension); ObDereferenceObject(extension->DeviceObject); ObDereferenceObject(filter->DeviceObject); return; } ExInitializeWorkItem(&context->WorkItem, VspGrowDiffArea, context); VspQueueWorkItem(filter->Root, &context->WorkItem, 0); VspDecrementVolumeRefCount(extension); ObDereferenceObject(filter->DeviceObject); } NTSTATUS VspAllocateDiffAreaSpace( IN PVOLUME_EXTENSION Extension, OUT PLONGLONG TargetOffset, OUT PLONGLONG FileOffset, IN OUT PDIFF_AREA_FILE_ALLOCATION* CurrentFileAllocation, IN OUT PLONGLONG CurrentOffset ) /*++ Routine Description: This routine allocates file space in a diff area file. The algorithm for this allocation is round robin which means that different size allocations can make the various files grow to be different sizes. The earmarked file is used and grown as necessary to get the space desired. Only if it is impossible to use the current file would the allocator go to the next one. If a file needs to be grown, the allocator will try to grow by 10 MB. Arguments: Extension - Supplies the volume extension. DiffAreaFile - Returns the diff area file used in the allocation. FileOffset - Returns the file offset in the diff area file used. Return Value: NTSTATUS Notes: Callers of this routine must be holding 'NonPagedResource'. --*/ { PFILTER_EXTENSION filter = Extension->Filter; LONGLONG targetOffset; PVSP_DIFF_AREA_FILE diffAreaFile; LONGLONG delta; PLIST_ENTRY l; PVSP_CONTEXT context; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; KIRQL irql; targetOffset = 0; diffAreaFile = Extension->DiffAreaFile; ASSERT(diffAreaFile); delta = 0; if (CurrentFileAllocation) { diffAreaFileAllocation = *CurrentFileAllocation; for (;;) { if (diffAreaFileAllocation->NLength - *CurrentOffset <= 0) { delta -= diffAreaFileAllocation->NLength - *CurrentOffset; *CurrentOffset = 0; l = diffAreaFileAllocation->ListEntry.Flink; if (l == &diffAreaFile->UnusedAllocationList) { break; } diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); continue; } targetOffset = diffAreaFileAllocation->Offset + *CurrentOffset; *CurrentFileAllocation = diffAreaFileAllocation; *CurrentOffset += BLOCK_SIZE; break; } } else { while (!IsListEmpty(&diffAreaFile->UnusedAllocationList)) { l = diffAreaFile->UnusedAllocationList.Flink; diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength <= 0) { delta -= diffAreaFileAllocation->NLength; RemoveEntryList(l); ExFreePool(diffAreaFileAllocation); continue; } targetOffset = diffAreaFileAllocation->Offset; diffAreaFileAllocation->Offset += BLOCK_SIZE; diffAreaFileAllocation->NLength -= BLOCK_SIZE; break; } } if (diffAreaFile->NextAvailable + delta + BLOCK_SIZE + Extension->DiffAreaFileIncrease <= diffAreaFile->AllocatedFileSize) { goto Finish; } if (diffAreaFile->NextAvailable + Extension->DiffAreaFileIncrease > diffAreaFile->AllocatedFileSize) { goto Finish; } context = VspAllocateContext(Extension->Root); if (!context) { if (!Extension->OkToGrowDiffArea) { VspLogError(Extension, diffAreaFile->Filter, VS_GROW_BEFORE_FREE_SPACE, STATUS_SUCCESS, 2, FALSE); } goto Finish; } KeAcquireSpinLock(&Extension->SpinLock, &irql); ASSERT(!Extension->GrowDiffAreaFilePending); ASSERT(IsListEmpty(&Extension->WaitingForDiffAreaSpace)); Extension->PastFileSystemOperations = FALSE; Extension->GrowDiffAreaFilePending = TRUE; KeReleaseSpinLock(&Extension->SpinLock, irql); context->Type = VSP_CONTEXT_TYPE_GROW_DIFF_AREA; ExInitializeWorkItem(&context->WorkItem, VspGrowDiffArea, context); context->GrowDiffArea.Extension = Extension; ObReferenceObject(Extension->DeviceObject); if (!Extension->OkToGrowDiffArea) { ObReferenceObject(filter->DeviceObject); ExInitializeWorkItem(&context->WorkItem, VspWaitForInstall, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); goto Finish; } VspQueueWorkItem(Extension->Root, &context->WorkItem, 0); Finish: if (targetOffset) { *TargetOffset = targetOffset; *FileOffset = diffAreaFile->NextAvailable + delta; } diffAreaFile->NextAvailable += delta; if (targetOffset) { diffAreaFile->NextAvailable += BLOCK_SIZE; } KeAcquireSpinLock(&filter->SpinLock, &irql); filter->UsedVolumeSpace += delta; if (targetOffset) { filter->UsedVolumeSpace += BLOCK_SIZE; } KeReleaseSpinLock(&filter->SpinLock, irql); return targetOffset ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; } VOID VspDecrementVolumeIrpRefCount( IN PVOID Irp ) { PIRP irp = (PIRP) Irp; PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp); PIO_STACK_LOCATION irpSp; PVOLUME_EXTENSION extension; if (InterlockedDecrement((PLONG) &nextSp->Parameters.Read.Length)) { return; } irpSp = IoGetCurrentIrpStackLocation(irp); extension = (PVOLUME_EXTENSION) irpSp->DeviceObject->DeviceExtension; ASSERT(extension->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); IoCompleteRequest(irp, IO_DISK_INCREMENT); VspDecrementVolumeRefCount(extension); } VOID VspDecrementIrpRefCount( IN PVOID Irp ) { PIRP irp = (PIRP) Irp; PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp); PIO_STACK_LOCATION irpSp; PFILTER_EXTENSION filter; PVOLUME_EXTENSION extension; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; KIRQL irql; PVSP_WRITE_CONTEXT writeContext; PDO_EXTENSION rootExtension; if (InterlockedDecrement((PLONG) &nextSp->Parameters.Read.Length)) { return; } irpSp = IoGetCurrentIrpStackLocation(irp); filter = (PFILTER_EXTENSION) irpSp->DeviceObject->DeviceExtension; ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER); extension = CONTAINING_RECORD(filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); diffAreaFile = extension->DiffAreaFile; ASSERT(diffAreaFile); VspDecrementRefCount(diffAreaFile->Filter); if (!irp->MdlAddress) { IoCompleteRequest(irp, IO_NO_INCREMENT); VspDecrementRefCount(filter); return; } writeContext = VspAllocateWriteContext(filter->Root); if (!writeContext) { rootExtension = filter->Root; KeAcquireSpinLock(&rootExtension->ESpinLock, &irql); if (rootExtension->EmergencyWriteContextInUse) { InsertTailList(&rootExtension->WriteContextIrpWaitingList, &irp->Tail.Overlay.ListEntry); if (!rootExtension->WriteContextIrpWaitingListNeedsChecking) { InterlockedExchange( &rootExtension->WriteContextIrpWaitingListNeedsChecking, TRUE); } KeReleaseSpinLock(&rootExtension->ESpinLock, irql); VspDecrementRefCount(filter); return; } rootExtension->EmergencyWriteContextInUse = TRUE; KeReleaseSpinLock(&rootExtension->ESpinLock, irql); writeContext = rootExtension->EmergencyWriteContext; } writeContext->Filter = filter; writeContext->Extension = extension; writeContext->Irp = irp; InitializeListHead(&writeContext->CompletionRoutines); KeAcquireSpinLock(&extension->SpinLock, &irql); InsertTailList(&extension->WriteContextList, &writeContext->ListEntry); KeReleaseSpinLock(&extension->SpinLock, irql); IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine(irp, VspWriteContextCompletionRoutine, writeContext, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, irp); } VOID VspDecrementIrpRefCountWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; PIRP irp = context->Extension.Irp; if (context->Type == VSP_CONTEXT_TYPE_WRITE_VOLUME) { ExInitializeWorkItem(&context->WorkItem, VspWriteVolume, context); VspAcquireNonPagedResource(extension, &context->WorkItem, TRUE); } else { ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(extension->Root, context); } VspDecrementIrpRefCount(irp); } VOID VspSignalCallback( IN PFILTER_EXTENSION Filter ) { KeSetEvent((PKEVENT) Filter->ZeroRefContext, IO_NO_INCREMENT, FALSE); } VOID VspCleanupVolumeSnapshot( IN PVOLUME_EXTENSION Extension, IN OUT PLIST_ENTRY ListOfDiffAreaFilesToClose, IN BOOLEAN KeepOnDisk ) /*++ Routine Description: This routine kills an existing volume snapshot. Arguments: Extension - Supplies the volume extension. Return Value: NTSTATUS Notes: Root->Semaphore required for calling this routine. --*/ { PFILTER_EXTENSION filter = Extension->Filter; PLIST_ENTRY l, ll; PVSP_DIFF_AREA_FILE diffAreaFile; KIRQL irql; POLD_HEAP_ENTRY oldHeapEntry; NTSTATUS status; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PVOID p; FILE_DISPOSITION_INFORMATION dispInfo; IO_STATUS_BLOCK ioStatus; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; VspAcquirePagedResource(Extension, NULL); if (Extension->DiffAreaFileMap) { status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, Extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); Extension->DiffAreaFileMap = NULL; } if (Extension->NextDiffAreaFileMap) { status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, Extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); Extension->NextDiffAreaFileMap = NULL; } while (!IsListEmpty(&Extension->OldHeaps)) { l = RemoveHeadList(&Extension->OldHeaps); oldHeapEntry = CONTAINING_RECORD(l, OLD_HEAP_ENTRY, ListEntry); status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, oldHeapEntry->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(oldHeapEntry); } VspReleasePagedResource(Extension); if (Extension->IsPersistent && !KeepOnDisk) { VspCleanupControlItemsForSnapshot(Extension); } if (Extension->DiffAreaFile) { diffAreaFile = Extension->DiffAreaFile; Extension->DiffAreaFile = NULL; KeAcquireSpinLock(&diffAreaFile->Filter->SpinLock, &irql); if (diffAreaFile->FilterListEntryBeingUsed) { RemoveEntryList(&diffAreaFile->FilterListEntry); diffAreaFile->FilterListEntryBeingUsed = FALSE; } KeReleaseSpinLock(&diffAreaFile->Filter->SpinLock, irql); KeAcquireSpinLock(&filter->SpinLock, &irql); filter->AllocatedVolumeSpace -= diffAreaFile->AllocatedFileSize; filter->UsedVolumeSpace -= diffAreaFile->NextAvailable; KeReleaseSpinLock(&filter->SpinLock, irql); while (!IsListEmpty(&diffAreaFile->UnusedAllocationList)) { ll = RemoveHeadList(&diffAreaFile->UnusedAllocationList); diffAreaFileAllocation = CONTAINING_RECORD(ll, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } if (diffAreaFile->TableUpdateIrp) { ExFreePool(MmGetMdlVirtualAddress( diffAreaFile->TableUpdateIrp->MdlAddress)); IoFreeMdl(diffAreaFile->TableUpdateIrp->MdlAddress); IoFreeIrp(diffAreaFile->TableUpdateIrp); } if (ListOfDiffAreaFilesToClose) { if (KeepOnDisk && Extension->IsPersistent) { lookupEntry = VspFindLookupTableItem( Extension->Root, &Extension->SnapshotGuid); ASSERT(lookupEntry); lookupEntry->DiffAreaHandle = diffAreaFile->FileHandle; ExFreePool(diffAreaFile); } else { InsertTailList(ListOfDiffAreaFilesToClose, &diffAreaFile->ListEntry); } } else { ASSERT(!diffAreaFile->FileHandle); ExFreePool(diffAreaFile); } } KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->VolumeBlockBitmap) { ExFreePool(Extension->VolumeBlockBitmap->Buffer); ExFreePool(Extension->VolumeBlockBitmap); Extension->VolumeBlockBitmap = NULL; } if (Extension->IgnorableProduct) { ExFreePool(Extension->IgnorableProduct->Buffer); ExFreePool(Extension->IgnorableProduct); Extension->IgnorableProduct = NULL; } KeReleaseSpinLock(&Extension->SpinLock, irql); VspAcquirePagedResource(Extension, NULL); if (Extension->ApplicationInformation) { Extension->ApplicationInformationSize = 0; ExFreePool(Extension->ApplicationInformation); Extension->ApplicationInformation = NULL; } VspReleasePagedResource(Extension); if (Extension->EmergencyCopyIrp) { ExFreePool(MmGetMdlVirtualAddress( Extension->EmergencyCopyIrp->MdlAddress)); IoFreeMdl(Extension->EmergencyCopyIrp->MdlAddress); IoFreeIrp(Extension->EmergencyCopyIrp); Extension->EmergencyCopyIrp = NULL; } VspDeleteWorkerThread(filter->Root); if (Extension->IgnoreCopyDataReference) { Extension->IgnoreCopyDataReference = FALSE; InterlockedDecrement(&filter->IgnoreCopyData); } } VOID VspEmptyIrpQueue( IN PDRIVER_OBJECT DriverObject, IN PLIST_ENTRY IrpQueue ) { PLIST_ENTRY l; PIRP irp; PIO_STACK_LOCATION irpSp; while (!IsListEmpty(IrpQueue)) { l = RemoveHeadList(IrpQueue); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); irpSp = IoGetCurrentIrpStackLocation(irp); DriverObject->MajorFunction[irpSp->MajorFunction](irpSp->DeviceObject, irp); } } VOID VspEmptyWorkerQueue( IN PLIST_ENTRY WorkerQueue ) { PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; while (!IsListEmpty(WorkerQueue)) { l = RemoveHeadList(WorkerQueue); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); workItem->WorkerRoutine(workItem->Parameter); } } VOID VspResumeSnapshotIo( IN PVOLUME_EXTENSION Extension ) { KIRQL irql; KeAcquireSpinLock(&Extension->SpinLock, &irql); InterlockedIncrement(&Extension->RefCount); InterlockedExchange(&Extension->HoldIncomingRequests, FALSE); KeReleaseSpinLock(&Extension->SpinLock, irql); } VOID VspPauseSnapshotIo( IN PVOLUME_EXTENSION Extension ) { KIRQL irql; VspReleaseWrites(Extension->Filter); KeAcquireSpinLock(&Extension->SpinLock, &irql); ASSERT(!Extension->HoldIncomingRequests); KeInitializeEvent(&Extension->ZeroRefEvent, NotificationEvent, FALSE); InterlockedExchange(&Extension->HoldIncomingRequests, TRUE); KeReleaseSpinLock(&Extension->SpinLock, irql); VspDecrementVolumeRefCount(Extension); KeWaitForSingleObject(&Extension->ZeroRefEvent, Executive, KernelMode, FALSE, NULL); } NTSTATUS VspDeleteOldestSnapshot( IN PFILTER_EXTENSION Filter, IN OUT PLIST_ENTRY ListOfDiffAreaFilesToClose, IN OUT PLIST_ENTRY LisfOfDeviceObjectsToDelete, IN BOOLEAN KeepOnDisk, IN BOOLEAN DontWakePnp ) /*++ Routine Description: This routine deletes the oldest volume snapshot on the given volume. Arguments: Filter - Supplies the filter extension. Return Value: NTSTATUS Notes: This routine assumes that Root->Semaphore is being held. --*/ { PFILTER_EXTENSION filter = Filter; PLIST_ENTRY l; PVOLUME_EXTENSION extension; KIRQL irql; if (IsListEmpty(&filter->VolumeList)) { return STATUS_INVALID_PARAMETER; } l = filter->VolumeList.Flink; extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); KeAcquireSpinLock(&extension->SpinLock, &irql); InterlockedExchange(&extension->IsDead, TRUE); InterlockedExchange(&extension->IsStarted, FALSE); KeReleaseSpinLock(&extension->SpinLock, irql); VspPauseSnapshotIo(extension); VspResumeSnapshotIo(extension); VspPauseVolumeIo(filter); ObReferenceObject(extension->DeviceObject); KeAcquireSpinLock(&filter->SpinLock, &irql); RemoveEntryList(&extension->ListEntry); if (IsListEmpty(&filter->VolumeList)) { InterlockedExchange(&filter->SnapshotsPresent, FALSE); } InterlockedIncrement(&Filter->EpicNumber); KeReleaseSpinLock(&filter->SpinLock, irql); VspResumeVolumeIo(filter); VspCleanupVolumeSnapshot(extension, ListOfDiffAreaFilesToClose, KeepOnDisk); if (extension->AliveToPnp) { InsertTailList(&filter->DeadVolumeList, &extension->ListEntry); if (!DontWakePnp) { IoInvalidateDeviceRelations(filter->Pdo, BusRelations); } } else { RtlDeleteElementGenericTable(&filter->Root->UsedDevnodeNumbers, &extension->DevnodeNumber); IoDeleteDevice(extension->DeviceObject); } InsertTailList(LisfOfDeviceObjectsToDelete, &extension->AnotherListEntry); return STATUS_SUCCESS; } VOID VspCloseDiffAreaFiles( IN PLIST_ENTRY ListOfDiffAreaFilesToClose, IN PLIST_ENTRY ListOfDeviceObjectsToDelete ) { PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; PVOLUME_EXTENSION extension; while (!IsListEmpty(ListOfDiffAreaFilesToClose)) { l = RemoveHeadList(ListOfDiffAreaFilesToClose); diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, ListEntry); ZwClose(diffAreaFile->FileHandle); ExFreePool(diffAreaFile); } while (!IsListEmpty(ListOfDeviceObjectsToDelete)) { l = RemoveHeadList(ListOfDeviceObjectsToDelete); extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, AnotherListEntry); ObDereferenceObject(extension->DeviceObject); } } VOID VspDestroyAllSnapshotsWorker( IN PVOID Context ) /*++ Routine Description: This routine will delete all of the snapshots in the system. Arguments: Filter - Supplies the filter extension. Return Value: None. --*/ { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->DestroyAllSnapshots.Filter; BOOLEAN keepOnDisk = context->DestroyAllSnapshots.KeepOnDisk; BOOLEAN synchronousCall = context->DestroyAllSnapshots.SynchronousCall; LIST_ENTRY listOfDiffAreaFilesToClose; LIST_ENTRY listOfDeviceObjectToDelete; BOOLEAN b; ASSERT(context->Type == VSP_CONTEXT_TYPE_DESTROY_SNAPSHOTS); if (!synchronousCall) { KeWaitForSingleObject(&filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); } InitializeListHead(&listOfDiffAreaFilesToClose); InitializeListHead(&listOfDeviceObjectToDelete); VspAcquire(filter->Root); b = FALSE; while (!IsListEmpty(&filter->VolumeList)) { VspDeleteOldestSnapshot(filter, &listOfDiffAreaFilesToClose, &listOfDeviceObjectToDelete, keepOnDisk, TRUE); b = TRUE; } if (b) { IoInvalidateDeviceRelations(filter->Pdo, BusRelations); } InterlockedExchange(&filter->DestroyAllSnapshotsPending, FALSE); VspRelease(filter->Root); VspCloseDiffAreaFiles(&listOfDiffAreaFilesToClose, &listOfDeviceObjectToDelete); ObDereferenceObject(filter->DeviceObject); } VOID VspAbortTableEntryWorker( IN PVOID TableEntry ) { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PIRP irp; PKEVENT event; if (extension->IsPersistent) { VspDeleteControlItemsWithGuid(extension->Filter, NULL, TRUE); } VspEmptyWorkerQueue(&tableEntry->WaitingQueueDpc); irp = tableEntry->WriteIrp; tableEntry->WriteIrp = NULL; if (irp) { VspDecrementIrpRefCount(irp); } event = tableEntry->WaitEvent; RtlDeleteElementGenericTable(&extension->TempVolumeBlockTable, tableEntry); if (event) { KeSetEvent(event, IO_NO_INCREMENT, FALSE); } VspReleaseNonPagedResource(extension); ObDereferenceObject(extension->Filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); } BOOLEAN VspDestroyAllSnapshots( IN PFILTER_EXTENSION Filter, IN PTEMP_TRANSLATION_TABLE_ENTRY TableEntry, IN BOOLEAN KeepOnDisk, IN BOOLEAN PerformSynchronously ) /*++ Routine Description: This routine will delete all of the snapshots in the system. Arguments: Filter - Supplies the filter extension. Return Value: None. --*/ { LONG destroyInProgress; KIRQL irql; PLIST_ENTRY l; PVOLUME_EXTENSION extension; PWORK_QUEUE_ITEM workItem; NTSTATUS status; PVSP_CONTEXT context; destroyInProgress = InterlockedExchange( &Filter->DestroyAllSnapshotsPending, TRUE); if (!destroyInProgress) { KeAcquireSpinLock(&Filter->SpinLock, &irql); for (l = Filter->VolumeList.Flink; l != &Filter->VolumeList; l = l->Flink) { extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); InterlockedExchange(&extension->IsStarted, FALSE); InterlockedExchange(&extension->IsDead, TRUE); } KeReleaseSpinLock(&Filter->SpinLock, irql); } if (TableEntry) { extension = TableEntry->Extension; if (TableEntry->IsMoveEntry) { ASSERT(TableEntry->WaitEvent); KeSetEvent(TableEntry->WaitEvent, IO_NO_INCREMENT, FALSE); } else { if (TableEntry->CopyIrp) { VspFreeCopyIrp(extension, TableEntry->CopyIrp); TableEntry->CopyIrp = NULL; } KeAcquireSpinLock(&extension->SpinLock, &irql); RtlSetBit(extension->VolumeBlockBitmap, (ULONG) (TableEntry->VolumeOffset>>BLOCK_SHIFT)); TableEntry->IsComplete = TRUE; if (TableEntry->InTableUpdateQueue) { TableEntry->InTableUpdateQueue = FALSE; RemoveEntryList(&TableEntry->TableUpdateListEntry); } KeReleaseSpinLock(&extension->SpinLock, irql); ObReferenceObject(extension->DeviceObject); ObReferenceObject(extension->Filter->DeviceObject); workItem = &TableEntry->WorkItem; ExInitializeWorkItem(workItem, VspAbortTableEntryWorker, TableEntry); VspAcquireNonPagedResource(extension, workItem, TRUE); } } if (destroyInProgress) { return FALSE; } context = &Filter->DestroyContext; context->Type = VSP_CONTEXT_TYPE_DESTROY_SNAPSHOTS; context->DestroyAllSnapshots.Filter = Filter; context->DestroyAllSnapshots.KeepOnDisk = KeepOnDisk; context->DestroyAllSnapshots.SynchronousCall = PerformSynchronously; ObReferenceObject(Filter->DeviceObject); ExInitializeWorkItem(&context->WorkItem, VspDestroyAllSnapshotsWorker, context); if (PerformSynchronously) { VspDestroyAllSnapshotsWorker(context); } else { ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } return TRUE; } VOID VspWriteVolumePhase5( IN PVOID TableEntry ) { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PKEVENT event; event = tableEntry->WaitEvent; RtlDeleteElementGenericTable(&extension->TempVolumeBlockTable, tableEntry); if (event) { KeSetEvent(event, IO_NO_INCREMENT, FALSE); } VspReleaseNonPagedResource(extension); VspDecrementVolumeRefCount(extension); } VOID VspWriteVolumePhase4( IN PVOID TableEntry ) /*++ Routine Description: This routine is queued from the completion of writting a block to make up a table entry for the write. This routine will create and insert the table entry. Arguments: Context - Supplies the context. Return Value: None. --*/ { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PFILTER_EXTENSION filter = extension->Filter; TRANSLATION_TABLE_ENTRY keyTableEntry; PTRANSLATION_TABLE_ENTRY backPointer, finalTableEntry; PVOID r; PVOID nodeOrParent; TABLE_SEARCH_RESULT searchResult; KIRQL irql; RtlZeroMemory(&keyTableEntry, sizeof(TRANSLATION_TABLE_ENTRY)); keyTableEntry.VolumeOffset = tableEntry->VolumeOffset; keyTableEntry.TargetObject = tableEntry->TargetObject; keyTableEntry.TargetOffset = tableEntry->TargetOffset; _try { backPointer = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable(&extension->CopyBackPointerTable, &keyTableEntry); if (backPointer) { keyTableEntry.VolumeOffset = backPointer->TargetOffset; } r = RtlLookupElementGenericTableFull(&extension->VolumeBlockTable, &keyTableEntry, &nodeOrParent, &searchResult); ASSERT(!backPointer || r); if (r) { ASSERT(backPointer); RtlDeleteElementGenericTable(&extension->CopyBackPointerTable, backPointer); finalTableEntry = (PTRANSLATION_TABLE_ENTRY) r; ASSERT(finalTableEntry->Flags& VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY); finalTableEntry->TargetObject = tableEntry->TargetObject; finalTableEntry->Flags &= ~VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY; finalTableEntry->TargetOffset = tableEntry->TargetOffset; } else { r = RtlInsertElementGenericTableFull( &extension->VolumeBlockTable, &keyTableEntry, sizeof(TRANSLATION_TABLE_ENTRY), NULL, nodeOrParent, searchResult); } } _except (EXCEPTION_EXECUTE_HANDLER) { r = NULL; } VspReleasePagedResource(extension); if (!r) { KeAcquireSpinLock(&extension->SpinLock, &irql); if (extension->PageFileSpaceCreatePending) { ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase4, tableEntry); InsertTailList(&extension->WaitingForPageFileSpace, &tableEntry->WorkItem.List); KeReleaseSpinLock(&extension->SpinLock, irql); return; } KeReleaseSpinLock(&extension->SpinLock, irql); if (VspDestroyAllSnapshots(filter, tableEntry, FALSE, FALSE)) { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_NO_HEAP, STATUS_SUCCESS, 0, FALSE); } VspDecrementVolumeRefCount(extension); return; } ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase5, tableEntry); VspAcquireNonPagedResource(extension, &tableEntry->WorkItem, FALSE); } VOID VspWriteVolumePhase35( IN PTEMP_TRANSLATION_TABLE_ENTRY TableEntry ) { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PFILTER_EXTENSION filter = extension->Filter; KIRQL irql; BOOLEAN emptyQueue; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; PVSP_CONTEXT context; PIRP irp; NTSTATUS status; KeAcquireSpinLock(&extension->SpinLock, &irql); RtlSetBit(extension->VolumeBlockBitmap, (ULONG) (tableEntry->VolumeOffset>>BLOCK_SHIFT)); tableEntry->IsComplete = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); while (!IsListEmpty(&tableEntry->WaitingQueueDpc)) { l = RemoveHeadList(&tableEntry->WaitingQueueDpc); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); context = (PVSP_CONTEXT) workItem->Parameter; if (context->Type == VSP_CONTEXT_TYPE_READ_SNAPSHOT) { context->ReadSnapshot.TargetObject = tableEntry->TargetObject; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = tableEntry->TargetOffset; } workItem->WorkerRoutine(workItem->Parameter); } irp = tableEntry->WriteIrp; tableEntry->WriteIrp = NULL; status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { VspDestroyAllSnapshots(filter, tableEntry, FALSE, FALSE); VspDecrementIrpRefCount(irp); return; } VspDecrementIrpRefCount(irp); ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase4, tableEntry); VspAcquirePagedResource(extension, &tableEntry->WorkItem); } VOID VspKillTableUpdates( IN PVSP_DIFF_AREA_FILE DiffAreaFile ) { PVSP_DIFF_AREA_FILE diffAreaFile = DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; KIRQL irql; LIST_ENTRY q; PLIST_ENTRY l; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry; ObReferenceObject(extension->DeviceObject); for (;;) { KeAcquireSpinLock(&extension->SpinLock, &irql); if (IsListEmpty(&diffAreaFile->TableUpdateQueue)) { diffAreaFile->TableUpdateInProgress = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); break; } l = RemoveHeadList(&diffAreaFile->TableUpdateQueue); tableEntry = CONTAINING_RECORD(l, TEMP_TRANSLATION_TABLE_ENTRY, TableUpdateListEntry); tableEntry->InTableUpdateQueue = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); VspDestroyAllSnapshots(extension->Filter, tableEntry, FALSE, FALSE); } VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); ObDereferenceObject(extension->DeviceObject); } NTSTATUS VspReadNextDiffAreaBlockCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) /*++ Routine Description: This routine is the completion for a read of the next diff area file table block. Arguments: DiffAreaFile - Supplies the diff area file. Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; if (!NT_SUCCESS(status)) { if (!diffAreaFile->Extension->Filter->DestroyAllSnapshotsPending) { VspLogError(diffAreaFile->Extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 8, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } diffAreaFile->NextFreeTableEntryOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; VspWriteTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspWriteTableUpdatesCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) /*++ Routine Description: This routine is the completion for a write to the diff area file table. Arguments: Context - Supplies the context. Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; NTSTATUS status = Irp->IoStatus.Status; PLIST_ENTRY l; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 9, FALSE); } while (!IsListEmpty(&diffAreaFile->TableUpdatesInProgress)) { l = RemoveHeadList(&diffAreaFile->TableUpdatesInProgress); tableEntry = CONTAINING_RECORD(l, TEMP_TRANSLATION_TABLE_ENTRY, TableUpdateListEntry); VspDestroyAllSnapshots(extension->Filter, tableEntry, FALSE, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } while (!IsListEmpty(&diffAreaFile->TableUpdatesInProgress)) { l = RemoveHeadList(&diffAreaFile->TableUpdatesInProgress); tableEntry = CONTAINING_RECORD(l, TEMP_TRANSLATION_TABLE_ENTRY, TableUpdateListEntry); if (tableEntry->IsMoveEntry) { ASSERT(tableEntry->WaitEvent); KeSetEvent(tableEntry->WaitEvent, IO_NO_INCREMENT, FALSE); } else { VspWriteVolumePhase35(tableEntry); } } VspWriteTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspCreateNewDiffAreaBlockPhase5( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) { NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; PIRP irp; PIO_STACK_LOCATION nextSp; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 21, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } VspWriteTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspCreateNewDiffAreaBlockPhase4( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) { NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; PIRP irp; PIO_STACK_LOCATION nextSp; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 20, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } diffAreaFile->TableTargetOffset = diffAreaFile->NextTableTargetOffset; diffAreaFile->NextFreeTableEntryOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; irp = diffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Read.ByteOffset.QuadPart = diffAreaFile->NextTableTargetOffset; nextSp->Parameters.Read.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = diffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCreateNewDiffAreaBlockPhase5, diffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspCreateNewDiffAreaBlockPhase3( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) { NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; PVSP_BLOCK_DIFF_AREA diffAreaBlock; PIRP irp; PIO_STACK_LOCATION nextSp; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 19, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress(diffAreaFile->TableUpdateIrp->MdlAddress); diffAreaBlock->Header.NextVolumeOffset = diffAreaFile->NextTableTargetOffset; irp = diffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = diffAreaFile->TableTargetOffset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = diffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCreateNewDiffAreaBlockPhase4, diffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspCreateNewDiffAreaBlockPhase2( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) { NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; PIRP irp; PIO_STACK_LOCATION nextSp; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 18, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } irp = diffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Read.ByteOffset.QuadPart = diffAreaFile->TableTargetOffset; nextSp->Parameters.Read.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = diffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCreateNewDiffAreaBlockPhase3, diffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspCreateNewDiffAreaBlockPhase15( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DiffAreaFile ) { NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; PVOID buffer = MmGetMdlVirtualAddress(Irp->MdlAddress); ULONG i; PIRP irp; PIO_STACK_LOCATION nextSp; PVSP_BLOCK_DIFF_AREA diffAreaBlock; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 100, FALSE); } VspKillTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } for (i = 0; i < BLOCK_SIZE; i += SMALLEST_NTFS_CLUSTER) { if (RtlCompareMemory((PCHAR) buffer + i, &VSP_DIFF_AREA_FILE_GUID, sizeof(GUID)) != sizeof(GUID)) { break; } } if (i < BLOCK_SIZE) { VspWriteTableUpdates(diffAreaFile); return STATUS_MORE_PROCESSING_REQUIRED; } diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress(diffAreaFile->TableUpdateIrp->MdlAddress); RtlZeroMemory(diffAreaBlock, BLOCK_SIZE); diffAreaBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; diffAreaBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; diffAreaBlock->Header.BlockType = VSP_BLOCK_TYPE_DIFF_AREA; diffAreaBlock->Header.ThisFileOffset = diffAreaFile->NextTableFileOffset; diffAreaBlock->Header.ThisVolumeOffset = diffAreaFile->NextTableTargetOffset; irp = diffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = diffAreaFile->NextTableTargetOffset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = diffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCreateNewDiffAreaBlockPhase2, diffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return STATUS_MORE_PROCESSING_REQUIRED; } VOID VspCreateNewDiffAreaBlockPhase1( IN PVSP_DIFF_AREA_FILE DiffAreaFile ) { PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; LONGLONG targetOffset = diffAreaFile->NextTableTargetOffset; LONGLONG fileOffset = diffAreaFile->NextTableFileOffset; PVSP_BLOCK_DIFF_AREA diffAreaBlock; PIRP irp; PIO_STACK_LOCATION nextSp; ASSERT(targetOffset); ASSERT(fileOffset); if (extension->IsDetected && !extension->OkToGrowDiffArea && !extension->NoDiffAreaFill) { irp = diffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = targetOffset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = diffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCreateNewDiffAreaBlockPhase15, diffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return; } diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress(diffAreaFile->TableUpdateIrp->MdlAddress); RtlZeroMemory(diffAreaBlock, BLOCK_SIZE); diffAreaBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; diffAreaBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; diffAreaBlock->Header.BlockType = VSP_BLOCK_TYPE_DIFF_AREA; diffAreaBlock->Header.ThisFileOffset = fileOffset; diffAreaBlock->Header.ThisVolumeOffset = targetOffset; irp = diffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = targetOffset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = diffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCreateNewDiffAreaBlockPhase2, diffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); } VOID VspCreateNewDiffAreaBlockAllocate( IN PVOID DiffAreaFile ) { PVSP_DIFF_AREA_FILE diffAreaFile = (PVSP_DIFF_AREA_FILE) DiffAreaFile; PVOLUME_EXTENSION extension = diffAreaFile->Extension; PFILTER_EXTENSION filter; KIRQL irql, irql2; BOOLEAN dontNeedWrite; diffAreaFile->StatusOfNextBlockAllocate = VspAllocateDiffAreaSpace(extension, &diffAreaFile->NextTableTargetOffset, &diffAreaFile->NextTableFileOffset, NULL, NULL); VspReleaseNonPagedResource(extension); if (!NT_SUCCESS(diffAreaFile->StatusOfNextBlockAllocate)) { filter = extension->Filter; KeAcquireSpinLock(&extension->SpinLock, &irql); if (extension->GrowDiffAreaFilePending) { KeAcquireSpinLock(&filter->SpinLock, &irql2); if ((IsListEmpty(&filter->DiffAreaFilesOnThisFilter) && !filter->FSRefCount && !diffAreaFile->Filter->SnapshotsPresent && !filter->UsedForPaging && extension->OkToGrowDiffArea) || extension->PastFileSystemOperations) { InsertTailList(&extension->WaitingForDiffAreaSpace, &diffAreaFile->WorkItem.List); KeReleaseSpinLock(&filter->SpinLock, irql2); KeReleaseSpinLock(&extension->SpinLock, irql); return; } KeReleaseSpinLock(&filter->SpinLock, irql2); } KeReleaseSpinLock(&extension->SpinLock, irql); } KeAcquireSpinLock(&extension->SpinLock, &irql); dontNeedWrite = diffAreaFile->TableUpdateInProgress; diffAreaFile->NextBlockAllocationComplete = TRUE; diffAreaFile->TableUpdateInProgress = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); if (!dontNeedWrite) { VspWriteTableUpdates(diffAreaFile); } } VOID VspWriteTableUpdates( IN PVSP_DIFF_AREA_FILE DiffAreaFile ) /*++ Routine Description: This routine adds the table entries into the diff area block and writes them to disk. As needed this routine re-iterates and allocates new diff area blocks. Arguments: DiffAreaFile - Supplies the diff area file. Return Value: None. --*/ { PVOLUME_EXTENSION extension = DiffAreaFile->Extension; KIRQL irql; PVSP_BLOCK_DIFF_AREA diffAreaBlock; PLIST_ENTRY l; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry; PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY diffAreaTableEntry; PIRP irp; PIO_STACK_LOCATION nextSp; KeAcquireSpinLock(&extension->SpinLock, &irql); if (DiffAreaFile->IrpNeeded) { DiffAreaFile->IrpNeeded = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); KeSetEvent(&DiffAreaFile->IrpReady, IO_NO_INCREMENT, FALSE); return; } if (IsListEmpty(&DiffAreaFile->TableUpdateQueue)) { DiffAreaFile->TableUpdateInProgress = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); return; } diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress(DiffAreaFile->TableUpdateIrp->MdlAddress); if (DiffAreaFile->NextFreeTableEntryOffset + sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY) > BLOCK_SIZE) { if (DiffAreaFile->NextBlockAllocationComplete) { DiffAreaFile->NextBlockAllocationComplete = FALSE; ASSERT(DiffAreaFile->NextBlockAllocationInProgress); DiffAreaFile->NextBlockAllocationInProgress = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); if (!NT_SUCCESS(DiffAreaFile->StatusOfNextBlockAllocate)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_OUT_OF_DIFF_AREA, STATUS_SUCCESS, 1, FALSE); } VspKillTableUpdates(DiffAreaFile); return; } VspCreateNewDiffAreaBlockPhase1(DiffAreaFile); return; } DiffAreaFile->TableUpdateInProgress = FALSE; if (DiffAreaFile->NextBlockAllocationInProgress) { KeReleaseSpinLock(&extension->SpinLock, irql); return; } DiffAreaFile->NextBlockAllocationInProgress = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); ASSERT(!diffAreaBlock->Header.NextVolumeOffset); ExInitializeWorkItem(&DiffAreaFile->WorkItem, VspCreateNewDiffAreaBlockAllocate, DiffAreaFile); VspAcquireNonPagedResource(extension, &DiffAreaFile->WorkItem, TRUE); return; } KeReleaseSpinLock(&extension->SpinLock, irql); InitializeListHead(&DiffAreaFile->TableUpdatesInProgress); for (;;) { if (DiffAreaFile->NextFreeTableEntryOffset + sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY) > BLOCK_SIZE) { break; } KeAcquireSpinLock(&extension->SpinLock, &irql); if (IsListEmpty(&DiffAreaFile->TableUpdateQueue)) { KeReleaseSpinLock(&extension->SpinLock, irql); break; } l = RemoveHeadList(&DiffAreaFile->TableUpdateQueue); tableEntry = CONTAINING_RECORD(l, TEMP_TRANSLATION_TABLE_ENTRY, TableUpdateListEntry); tableEntry->InTableUpdateQueue = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); InsertTailList(&DiffAreaFile->TableUpdatesInProgress, l); diffAreaTableEntry = (PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY) ((PCHAR) diffAreaBlock + DiffAreaFile->NextFreeTableEntryOffset); DiffAreaFile->NextFreeTableEntryOffset += sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY); if (tableEntry->IsMoveEntry) { diffAreaTableEntry->SnapshotVolumeOffset = tableEntry->FileOffset; diffAreaTableEntry->DiffAreaFileOffset = tableEntry->VolumeOffset; diffAreaTableEntry->Flags |= VSP_DIFF_AREA_TABLE_ENTRY_FLAG_MOVE_ENTRY; } else { diffAreaTableEntry->SnapshotVolumeOffset = tableEntry->VolumeOffset; diffAreaTableEntry->DiffAreaFileOffset = tableEntry->FileOffset; diffAreaTableEntry->DiffAreaVolumeOffset = tableEntry->TargetOffset; } } irp = DiffAreaFile->TableUpdateIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = diffAreaBlock->Header.ThisVolumeOffset; ASSERT(diffAreaBlock->Header.ThisVolumeOffset == DiffAreaFile->TableTargetOffset); nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = DiffAreaFile->Filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspWriteTableUpdatesCompletion, DiffAreaFile, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); } NTSTATUS VspWriteVolumePhase3( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID TableEntry ) /*++ Routine Description: This routine is the completion for a write to the diff area file. Arguments: Context - Supplies the context. Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PFILTER_EXTENSION filter = extension->Filter; NTSTATUS status = Irp->IoStatus.Status; PVSP_DIFF_AREA_FILE diffAreaFile; KIRQL irql; BOOLEAN dontNeedWrite; if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(extension, extension->DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 10, FALSE); } VspDestroyAllSnapshots(filter, tableEntry, FALSE, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } VspFreeCopyIrp(extension, Irp); tableEntry->CopyIrp = NULL; if (extension->IsPersistent) { diffAreaFile = extension->DiffAreaFile; KeAcquireSpinLock(&extension->SpinLock, &irql); InsertTailList(&diffAreaFile->TableUpdateQueue, &tableEntry->TableUpdateListEntry); tableEntry->InTableUpdateQueue = TRUE; dontNeedWrite = diffAreaFile->TableUpdateInProgress; diffAreaFile->TableUpdateInProgress = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); if (!dontNeedWrite) { VspWriteTableUpdates(diffAreaFile); } return STATUS_MORE_PROCESSING_REQUIRED; } VspWriteVolumePhase35(tableEntry); return STATUS_MORE_PROCESSING_REQUIRED; } VOID VspWriteVolumePhase25( IN PVOID TableEntry ) { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PIRP irp = tableEntry->CopyIrp; PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp); PVOLUME_EXTENSION extension = tableEntry->Extension; NTSTATUS status; PFILTER_EXTENSION filter; PVSP_DIFF_AREA_FILE diffAreaFile; KIRQL irql, irql2; status = VspAllocateDiffAreaSpace(extension, &tableEntry->TargetOffset, &tableEntry->FileOffset, NULL, NULL); VspReleaseNonPagedResource(extension); if (!NT_SUCCESS(status)) { diffAreaFile = extension->DiffAreaFile; filter = extension->Filter; KeAcquireSpinLock(&extension->SpinLock, &irql); if (extension->GrowDiffAreaFilePending) { KeAcquireSpinLock(&filter->SpinLock, &irql2); if ((IsListEmpty(&filter->DiffAreaFilesOnThisFilter) && !filter->FSRefCount && !diffAreaFile->Filter->SnapshotsPresent && !filter->UsedForPaging && extension->OkToGrowDiffArea) || extension->PastFileSystemOperations) { ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase25, tableEntry); InsertTailList(&extension->WaitingForDiffAreaSpace, &tableEntry->WorkItem.List); KeReleaseSpinLock(&filter->SpinLock, irql2); KeReleaseSpinLock(&extension->SpinLock, irql); return; } KeReleaseSpinLock(&filter->SpinLock, irql2); } KeReleaseSpinLock(&extension->SpinLock, irql); ObReferenceObject(extension->DeviceObject); ObReferenceObject(filter->DeviceObject); if (VspDestroyAllSnapshots(filter, tableEntry, FALSE, FALSE)) { if (extension->GrowFailed) { if (extension->UserImposedLimit) { VspLogError(extension, NULL, VS_ABORT_NO_DIFF_AREA_SPACE_USER_IMPOSED, STATUS_SUCCESS, 2, FALSE); } else { VspLogError(extension, NULL, VS_ABORT_NO_DIFF_AREA_SPACE_GROW_FAILED, STATUS_SUCCESS, 0, FALSE); } } else { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_OUT_OF_DIFF_AREA, STATUS_SUCCESS, 3, FALSE); } } ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); return; } irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = tableEntry->TargetOffset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = tableEntry->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspWriteVolumePhase3, tableEntry, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); } NTSTATUS VspWriteVolumePhase2( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID TableEntry ) /*++ Routine Description: This routine is the completion for a read who's data will create the table entry for the block that is being written to. Arguments: Context - Supplies the context. Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { NTSTATUS status = Irp->IoStatus.Status; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PIRP irp; PIO_STACK_LOCATION nextSp; if (!NT_SUCCESS(status)) { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, extension->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 11, FALSE); } VspDestroyAllSnapshots(extension->Filter, tableEntry, FALSE, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } if (tableEntry->TargetOffset) { irp = tableEntry->CopyIrp; nextSp = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp->Parameters.Write.ByteOffset.QuadPart = tableEntry->TargetOffset; nextSp->Parameters.Write.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = tableEntry->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspWriteVolumePhase3, tableEntry, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return STATUS_MORE_PROCESSING_REQUIRED; } ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase25, tableEntry); VspAcquireNonPagedResource(extension, &tableEntry->WorkItem, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspWriteVolumePhase15( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID TableEntry ) { NTSTATUS status = Irp->IoStatus.Status; PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp); PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PFILTER_EXTENSION filter = extension->Filter; PVOID buffer = MmGetMdlVirtualAddress(Irp->MdlAddress); ULONG i; if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(extension, extension->DiffAreaFile->Filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 12, FALSE); } VspDestroyAllSnapshots(filter, tableEntry, FALSE, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } for (i = 0; i < BLOCK_SIZE; i += SMALLEST_NTFS_CLUSTER) { if (RtlCompareMemory((PCHAR) buffer + i, &VSP_DIFF_AREA_FILE_GUID, sizeof(GUID)) != sizeof(GUID)) { break; } } if (i < BLOCK_SIZE) { tableEntry->TargetOffset = 0; ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase12, tableEntry); VspAcquireNonPagedResource(extension, &tableEntry->WorkItem, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } Irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp = IoGetNextIrpStackLocation(Irp); nextSp->Parameters.Read.ByteOffset.QuadPart = tableEntry->VolumeOffset; nextSp->Parameters.Read.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; if (tableEntry->VolumeOffset + BLOCK_SIZE > extension->VolumeSize) { if (extension->VolumeSize > tableEntry->VolumeOffset) { nextSp->Parameters.Read.Length = (ULONG) (extension->VolumeSize - tableEntry->VolumeOffset); } } IoSetCompletionRoutine(Irp, VspWriteVolumePhase2, tableEntry, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, Irp); return STATUS_MORE_PROCESSING_REQUIRED; } VOID VspWriteVolumePhase12( IN PVOID TableEntry ) { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PFILTER_EXTENSION filter = extension->Filter; PIRP irp = tableEntry->CopyIrp; NTSTATUS status; PIO_STACK_LOCATION nextSp; if (tableEntry->TargetOffset) { status = STATUS_SUCCESS; } else { status = VspAllocateDiffAreaSpace(extension, &tableEntry->TargetOffset, &tableEntry->FileOffset, NULL, NULL); } VspReleaseNonPagedResource(extension); if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_OUT_OF_DIFF_AREA, status, 4, FALSE); } VspDestroyAllSnapshots(filter, tableEntry, FALSE, FALSE); return; } irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp = IoGetNextIrpStackLocation(irp); nextSp->Parameters.Read.ByteOffset.QuadPart = tableEntry->TargetOffset; nextSp->Parameters.Read.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = tableEntry->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspWriteVolumePhase15, tableEntry, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); } VOID VspWriteVolumePhase1( IN PVOID TableEntry ) /*++ Routine Description: This routine is the first phase of copying volume data to the diff area file. An irp and buffer are created for the initial read of the block. Arguments: Context - Supplies the context. Return Value: None. --*/ { PTEMP_TRANSLATION_TABLE_ENTRY tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) TableEntry; PVOLUME_EXTENSION extension = tableEntry->Extension; PFILTER_EXTENSION filter = extension->Filter; PIRP irp = tableEntry->CopyIrp; PIO_STACK_LOCATION nextSp; irp->Flags &= ~(IRP_HIGH_PRIORITY_PAGING_IO | IRP_PAGING_IO); irp->Flags |= (tableEntry->WriteIrp->Flags& (IRP_HIGH_PRIORITY_PAGING_IO | IRP_PAGING_IO)); if (extension->IsDetected && !extension->OkToGrowDiffArea && !extension->NoDiffAreaFill) { ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase12, tableEntry); VspAcquireNonPagedResource(extension, &tableEntry->WorkItem, FALSE); return; } irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp = IoGetNextIrpStackLocation(irp); nextSp->Parameters.Read.ByteOffset.QuadPart = tableEntry->VolumeOffset; nextSp->Parameters.Read.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; if (tableEntry->VolumeOffset + BLOCK_SIZE > extension->VolumeSize) { if (extension->VolumeSize > tableEntry->VolumeOffset) { nextSp->Parameters.Read.Length = (ULONG) (extension->VolumeSize - tableEntry->VolumeOffset); } } IoSetCompletionRoutine(irp, VspWriteVolumePhase2, tableEntry, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); } VOID VspUnmapNextDiffAreaFileMap( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; NTSTATUS status; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(extension->Root, context); if (extension->NextDiffAreaFileMap) { status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); extension->NextDiffAreaFileMap = NULL; } VspReleasePagedResource(extension); ObDereferenceObject(extension->Filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); } NTSTATUS VspTruncatePreviousDiffArea( IN PVOLUME_EXTENSION Extension ) /*++ Routine Description: This routine takes the snapshot that occurred before this one and truncates its diff area file to the current used size since diff area files can't grow after a new snapshot is added on top. Arguments: Extension - Supplies the volume extension. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = Extension->Filter; PLIST_ENTRY l, ll; PVOLUME_EXTENSION extension; PVSP_DIFF_AREA_FILE diffAreaFile; NTSTATUS status; LONGLONG diff; KIRQL irql; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PVSP_CONTEXT context; l = Extension->ListEntry.Blink; if (l == &filter->VolumeList) { return STATUS_SUCCESS; } extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); diffAreaFile = extension->DiffAreaFile; ASSERT(diffAreaFile); status = VspSetFileSize(diffAreaFile->FileHandle, diffAreaFile->NextAvailable); VspAcquireNonPagedResource(extension, NULL, FALSE); if (NT_SUCCESS(status)) { diff = diffAreaFile->AllocatedFileSize - diffAreaFile->NextAvailable; diffAreaFile->AllocatedFileSize -= diff; KeAcquireSpinLock(&filter->SpinLock, &irql); filter->AllocatedVolumeSpace -= diff; KeReleaseSpinLock(&filter->SpinLock, irql); } while (!IsListEmpty(&diffAreaFile->UnusedAllocationList)) { ll = RemoveHeadList(&diffAreaFile->UnusedAllocationList); diffAreaFileAllocation = CONTAINING_RECORD(ll, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } if (diffAreaFile->TableUpdateIrp) { ExFreePool(MmGetMdlVirtualAddress( diffAreaFile->TableUpdateIrp->MdlAddress)); IoFreeMdl(diffAreaFile->TableUpdateIrp->MdlAddress); IoFreeIrp(diffAreaFile->TableUpdateIrp); diffAreaFile->TableUpdateIrp = NULL; } VspReleaseNonPagedResource(extension); if (extension->EmergencyCopyIrp) { ExFreePool(MmGetMdlVirtualAddress( extension->EmergencyCopyIrp->MdlAddress)); IoFreeMdl(extension->EmergencyCopyIrp->MdlAddress); IoFreeIrp(extension->EmergencyCopyIrp); extension->EmergencyCopyIrp = NULL; } context = VspAllocateContext(Extension->Root); if (context) { context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = extension; ExInitializeWorkItem(&context->WorkItem, VspUnmapNextDiffAreaFileMap, context); ObReferenceObject(extension->DeviceObject); ObReferenceObject(extension->Filter->DeviceObject); VspAcquirePagedResource(extension, &context->WorkItem); } if (extension->IgnoreCopyDataReference) { extension->IgnoreCopyDataReference = FALSE; InterlockedDecrement(&filter->IgnoreCopyData); } return STATUS_SUCCESS; } VOID VspOrBitmaps( IN OUT PRTL_BITMAP BaseBitmap, IN PRTL_BITMAP FactorBitmap ) { ULONG n, i; PULONG p, q; n = (BaseBitmap->SizeOfBitMap + 8*sizeof(ULONG) - 1)/(8*sizeof(ULONG)); p = BaseBitmap->Buffer; q = FactorBitmap->Buffer; for (i = 0; i < n; i++) { *p++ |= *q++; } } NTSTATUS VspQueryFileOffset( IN PLIST_ENTRY ExtentList, IN LONGLONG VolumeOffset, IN PDIFF_AREA_FILE_ALLOCATION StartFileAllocation, IN LONGLONG StartFileAllocationFileOffset, OUT PLONGLONG ResultFileOffset, OUT PDIFF_AREA_FILE_ALLOCATION* ResultFileAllocation, OUT PLONGLONG ResultFileAllocationFileOffset, OUT PLONGLONG Length ) { PLIST_ENTRY l, start; LONGLONG fileOffset, delta; PDIFF_AREA_FILE_ALLOCATION diffAreaAllocation; ASSERT(!IsListEmpty(ExtentList)); if (!StartFileAllocation) { StartFileAllocation = CONTAINING_RECORD(ExtentList->Flink, DIFF_AREA_FILE_ALLOCATION, ListEntry); StartFileAllocationFileOffset = 0; } ASSERT(&StartFileAllocation->ListEntry != ExtentList); start = &StartFileAllocation->ListEntry; l = start; fileOffset = StartFileAllocationFileOffset; for (;;) { diffAreaAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaAllocation->NLength <= 0) { fileOffset -= diffAreaAllocation->NLength; } else { if (diffAreaAllocation->Offset <= VolumeOffset && diffAreaAllocation->Offset + diffAreaAllocation->NLength > VolumeOffset) { delta = VolumeOffset - diffAreaAllocation->Offset; *ResultFileOffset = fileOffset + delta; *ResultFileAllocation = diffAreaAllocation; *ResultFileAllocationFileOffset = fileOffset; if (Length) { *Length = diffAreaAllocation->NLength - delta; } return STATUS_SUCCESS; } fileOffset += diffAreaAllocation->NLength; } l = l->Flink; if (l == ExtentList) { fileOffset = 0; l = l->Flink; } if (l == start) { break; } } return STATUS_INVALID_PARAMETER; } NTSTATUS VspCheckBlockChainFileOffsets( IN PFILTER_EXTENSION Filter, IN LONGLONG StartingVolumeOffset, IN PLIST_ENTRY ExtentList ) { PVSP_BLOCK_HEADER blockHeader; LONGLONG volumeOffset, fileOffset; NTSTATUS status; PDIFF_AREA_FILE_ALLOCATION startFileAllocation, resultFileAllocation; LONGLONG startFileAllocationFileOffset; LONGLONG resultFileAllocationFileOffset; if (IsListEmpty(ExtentList) || !StartingVolumeOffset) { return STATUS_FILE_CORRUPT_ERROR; } VspAcquireNonPagedResource(Filter, NULL, FALSE); if (!Filter->SnapshotOnDiskIrp) { VspReleaseNonPagedResource(Filter); return STATUS_INVALID_PARAMETER; } blockHeader = (PVSP_BLOCK_HEADER) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); volumeOffset = StartingVolumeOffset; startFileAllocation = NULL; startFileAllocationFileOffset = 0; while (volumeOffset) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, volumeOffset, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); return status; } status = VspQueryFileOffset(ExtentList, volumeOffset, startFileAllocation, startFileAllocationFileOffset, &fileOffset, &resultFileAllocation, &resultFileAllocationFileOffset, NULL); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); return status; } if (fileOffset != blockHeader->ThisFileOffset) { ASSERT(FALSE); VspReleaseNonPagedResource(Filter); return STATUS_FILE_CORRUPT_ERROR; } volumeOffset = blockHeader->NextVolumeOffset; startFileAllocation = resultFileAllocation; startFileAllocationFileOffset = resultFileAllocationFileOffset; } VspReleaseNonPagedResource(Filter); return status; } NTSTATUS VspCheckControlBlockFileLocation( IN PFILTER_EXTENSION Filter ) { HANDLE h = Filter->ControlBlockFileHandle; NTSTATUS status; LIST_ENTRY extentList; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; if (!h) { return STATUS_INVALID_PARAMETER; } status = VspQueryListOfExtents(h, 0, &extentList, NULL, FALSE); if (!NT_SUCCESS(status)) { return status; } status = VspCheckBlockChainFileOffsets(Filter, Filter->FirstControlBlockVolumeOffset, &extentList); while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } return status; } VOID VspWaitForVolumesSafeForWriteAccess( IN PDO_EXTENSION RootExtension ) { UNICODE_STRING volumeSafeEventName; OBJECT_ATTRIBUTES oa; KEVENT event; LARGE_INTEGER timeout; ULONG i; NTSTATUS status; HANDLE volumeSafeEvent; if (RootExtension->IsSetup || RootExtension->VolumesSafeForWriteAccess) { return; } RtlInitUnicodeString(&volumeSafeEventName, L"\\Device\\VolumesSafeForWriteAccess"); InitializeObjectAttributes(&oa, &volumeSafeEventName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent(&event, NotificationEvent, FALSE); timeout.QuadPart = -10*1000*1000; // 1 second for (i = 0; i < 1000; i++) { status = ZwOpenEvent(&volumeSafeEvent, EVENT_ALL_ACCESS, &oa); if (NT_SUCCESS(status)) { break; } KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout); if (RootExtension->VolumesSafeForWriteAccess) { return; } } if (i == 1000) { return; } for (;;) { status = ZwWaitForSingleObject(volumeSafeEvent, FALSE, &timeout); if (status != STATUS_TIMEOUT) { InterlockedExchange(&RootExtension->VolumesSafeForWriteAccess, TRUE); break; } if (RootExtension->VolumesSafeForWriteAccess) { break; } } ZwClose(volumeSafeEvent); } NTSTATUS VspOpenControlBlockFile( IN PFILTER_EXTENSION Filter ) { PFILTER_EXTENSION filter = Filter; NTSTATUS status; UNICODE_STRING fileName; PWCHAR star; OBJECT_ATTRIBUTES oa; HANDLE h; IO_STATUS_BLOCK ioStatus; PLIST_ENTRY l; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; GUID snapshotGuid; ULONG i; LARGE_INTEGER timeout; KEVENT event; if (filter->ControlBlockFileHandle) { return STATUS_SUCCESS; } VspWaitForVolumesSafeForWriteAccess(filter->Root); KeWaitForSingleObject(&filter->ControlBlockFileHandleSemaphore, Executive, KernelMode, FALSE, NULL); if (filter->ControlBlockFileHandle) { KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } VspAcquireNonPagedResource(filter, NULL, FALSE); VspCreateStartBlock(filter, filter->FirstControlBlockVolumeOffset, filter->MaximumVolumeSpace); VspReleaseNonPagedResource(filter); status = VspCreateDiffAreaFileName(filter->TargetObject, NULL, &fileName, FALSE, NULL); if (!NT_SUCCESS(status)) { KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } star = fileName.Buffer + fileName.Length/sizeof(WCHAR) - 39; RtlMoveMemory(star, star + 1, 38*sizeof(WCHAR)); fileName.Length -= sizeof(WCHAR); fileName.Buffer[fileName.Length/sizeof(WCHAR)] = 0; InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent(&event, NotificationEvent, FALSE); timeout.QuadPart = -10*1000; // 1 millisecond. for (i = 0; i < 5000; i++) { status = ZwOpenFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &oa, &ioStatus, 0, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION); if (NT_SUCCESS(status)) { break; } // Wait for RAW to DISMOUNT or for the DISMOUNT handle to close. if (status != STATUS_INVALID_PARAMETER && status != STATUS_ACCESS_DENIED) { break; } KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout); } ExFreePool(fileName.Buffer); if (!NT_SUCCESS(status)) { KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } ASSERT(!filter->ControlBlockFileHandle); InterlockedExchangePointer(&filter->ControlBlockFileHandle, h); VspAcquire(filter->Root); if (filter->IsRemoved) { VspRelease(filter->Root); h = InterlockedExchangePointer(&filter->ControlBlockFileHandle, NULL); if (h) { ZwClose(h); } KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_UNSUCCESSFUL; } VspRelease(filter->Root); status = VspPinFile(filter->TargetObject, h); if (!NT_SUCCESS(status)) { h = InterlockedExchangePointer(&filter->ControlBlockFileHandle, NULL); if (h) { ZwClose(h); } KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } status = VspCheckControlBlockFileLocation(filter); if (!NT_SUCCESS(status)) { h = InterlockedExchangePointer(&filter->ControlBlockFileHandle, NULL); if (h) { ZwClose(h); } KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return status; } VspAcquireNonPagedResource(filter, NULL, FALSE); if (!filter->FirstControlBlockVolumeOffset) { h = InterlockedExchangePointer(&filter->ControlBlockFileHandle, NULL); VspReleaseNonPagedResource(filter); if (h) { ZwClose(h); } KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_UNSUCCESSFUL; } VspReleaseNonPagedResource(filter); KeReleaseSemaphore(&filter->ControlBlockFileHandleSemaphore, IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } VOID VspOpenControlBlockFileWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; NTSTATUS status; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); KeWaitForSingleObject(&filter->Root->PastBootReinit, Executive, KernelMode, FALSE, NULL); status = VspOpenControlBlockFile(filter); if (!NT_SUCCESS(status)) { VspAcquireNonPagedResource(filter, NULL, FALSE); VspCreateStartBlock(filter, 0, filter->MaximumVolumeSpace); filter->FirstControlBlockVolumeOffset = 0; VspReleaseNonPagedResource(filter); } VspFreeContext(filter->Root, context); ObDereferenceObject(filter->TargetObject); ObDereferenceObject(filter->DeviceObject); } NTSTATUS VspCheckDiffAreaFileLocation( IN PFILTER_EXTENSION Filter, IN HANDLE FileHandle, IN LONGLONG ApplicationInfoOffset, IN LONGLONG FirstDiffAreaBlockOffset, IN LONGLONG LocationOffset, IN LONGLONG InitialBitmapOffset, IN BOOLEAN CheckUnused ) { NTSTATUS status; LIST_ENTRY extentList; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PVSP_BLOCK_HEADER blockHeader; LONGLONG volumeOffset, fileOffset; PDIFF_AREA_FILE_ALLOCATION startFileAllocation, resultFileAllocation; LONGLONG startFileAllocationFileOffset; LONGLONG resultFileAllocationFileOffset; PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY diffAreaTableEntry; ULONG blockOffset; PVSP_DIFF_AREA_LOCATION_DESCRIPTOR locationDescriptor; LONGLONG length; if (!FileHandle) { return STATUS_INVALID_PARAMETER; } status = VspQueryListOfExtents(FileHandle, 0, &extentList, NULL, FALSE); if (!NT_SUCCESS(status)) { ASSERT(FALSE); return status; } status = VspCheckBlockChainFileOffsets(Filter, ApplicationInfoOffset, &extentList); if (!NT_SUCCESS(status)) { goto Finish; } status = VspCheckBlockChainFileOffsets(Filter, FirstDiffAreaBlockOffset, &extentList); if (!NT_SUCCESS(status)) { goto Finish; } status = VspCheckBlockChainFileOffsets(Filter, LocationOffset, &extentList); if (!NT_SUCCESS(status)) { goto Finish; } if (InitialBitmapOffset) { status = VspCheckBlockChainFileOffsets(Filter, InitialBitmapOffset, &extentList); if (!NT_SUCCESS(status)) { goto Finish; } } VspAcquireNonPagedResource(Filter, NULL, FALSE); if (!Filter->SnapshotOnDiskIrp) { VspReleaseNonPagedResource(Filter); status = STATUS_INVALID_PARAMETER; goto Finish; } blockHeader = (PVSP_BLOCK_HEADER) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); volumeOffset = FirstDiffAreaBlockOffset; startFileAllocation = NULL; startFileAllocationFileOffset = 0; while (volumeOffset) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, volumeOffset, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); goto Finish; } for (blockOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; blockOffset + sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY) <= BLOCK_SIZE; blockOffset += sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY)) { diffAreaTableEntry = (PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY) ((PCHAR) blockHeader + blockOffset); if (!diffAreaTableEntry->DiffAreaVolumeOffset) { continue; } status = VspQueryFileOffset( &extentList, diffAreaTableEntry->DiffAreaVolumeOffset, startFileAllocation, startFileAllocationFileOffset, &fileOffset, &resultFileAllocation, &resultFileAllocationFileOffset, NULL); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); goto Finish; } if (fileOffset != diffAreaTableEntry->DiffAreaFileOffset) { status = STATUS_FILE_CORRUPT_ERROR; ASSERT(FALSE); VspReleaseNonPagedResource(Filter); goto Finish; } startFileAllocation = resultFileAllocation; startFileAllocationFileOffset = resultFileAllocationFileOffset; } volumeOffset = blockHeader->NextVolumeOffset; } if (!CheckUnused) { VspReleaseNonPagedResource(Filter); goto Finish; } blockHeader = (PVSP_BLOCK_HEADER) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); volumeOffset = LocationOffset; startFileAllocation = NULL; startFileAllocationFileOffset = 0; while (volumeOffset) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, volumeOffset, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); goto Finish; } for (blockOffset = VSP_OFFSET_TO_FIRST_LOCATION_DESCRIPTOR; blockOffset + sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR) <= BLOCK_SIZE; blockOffset += sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR)) { locationDescriptor = (PVSP_DIFF_AREA_LOCATION_DESCRIPTOR) ((PCHAR) blockHeader + blockOffset); if (!locationDescriptor->VolumeOffset) { continue; } status = VspQueryFileOffset( &extentList, locationDescriptor->VolumeOffset, startFileAllocation, startFileAllocationFileOffset, &fileOffset, &resultFileAllocation, &resultFileAllocationFileOffset, &length); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); goto Finish; } if (fileOffset != locationDescriptor->FileOffset || length < locationDescriptor->Length) { status = STATUS_FILE_CORRUPT_ERROR; ASSERT(FALSE); VspReleaseNonPagedResource(Filter); goto Finish; } startFileAllocation = resultFileAllocation; startFileAllocationFileOffset = resultFileAllocationFileOffset; } volumeOffset = blockHeader->NextVolumeOffset; } VspReleaseNonPagedResource(Filter); Finish: while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } return status; } NTSTATUS VspOpenFilesAndValidateSnapshots( IN PFILTER_EXTENSION Filter ) { NTSTATUS status; PLIST_ENTRY l; UNICODE_STRING fileName; OBJECT_ATTRIBUTES oa; HANDLE h; IO_STATUS_BLOCK ioStatus; PVOLUME_EXTENSION extension; PFILTER_EXTENSION diffAreaFilter; LONGLONG appInfoOffset, firstDiffAreaBlockOffset, locationOffset, initialBitmapOffset; BOOLEAN checkUnused; ULONG i; LARGE_INTEGER timeout; KEVENT event; status = VspOpenControlBlockFile(Filter); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_FAILURE_TO_OPEN_CRITICAL_FILE, status, 1, FALSE); return status; } status = STATUS_SUCCESS; for (;;) { VspAcquire(Filter->Root); for (l = Filter->VolumeList.Flink; l != &Filter->VolumeList; l = l->Flink) { extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); if (extension->DiffAreaFile && !extension->DiffAreaFile->FileHandle) { break; } } if (l == &Filter->VolumeList) { VspRelease(Filter->Root); break; } diffAreaFilter = extension->DiffAreaFile->Filter; appInfoOffset = extension->DiffAreaFile->ApplicationInfoTargetOffset; locationOffset = extension->DiffAreaFile->DiffAreaLocationDescriptionTargetOffset; firstDiffAreaBlockOffset = extension->DiffAreaFile->FirstTableTargetOffset; initialBitmapOffset = extension->DiffAreaFile->InitialBitmapVolumeOffset; ObReferenceObject(extension->DeviceObject); ObReferenceObject(diffAreaFilter->DeviceObject); ObReferenceObject(diffAreaFilter->TargetObject); VspRelease(Filter->Root); status = VspCreateDiffAreaFileName( diffAreaFilter->TargetObject, extension, &fileName, FALSE, NULL); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_FAILURE_TO_OPEN_CRITICAL_FILE, status, 2, FALSE); ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); ObDereferenceObject(extension->DeviceObject); break; } InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent(&event, NotificationEvent, FALSE); timeout.QuadPart = -10*1000; // 1 millisecond. for (i = 0; i < 5000; i++) { status = ZwOpenFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &oa, &ioStatus, 0, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION); if (NT_SUCCESS(status)) { break; } if (status != STATUS_ACCESS_DENIED && status != STATUS_INVALID_PARAMETER) { break; } KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout); } ExFreePool(fileName.Buffer); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_FAILURE_TO_OPEN_CRITICAL_FILE, status, 3, FALSE); ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); ObDereferenceObject(extension->DeviceObject); break; } status = VspPinFile(diffAreaFilter->TargetObject, h); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_FAILURE_TO_OPEN_CRITICAL_FILE, status, 4, FALSE); ZwClose(h); ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); ObDereferenceObject(extension->DeviceObject); break; } if (extension->ListEntry.Flink == &Filter->VolumeList) { checkUnused = TRUE; } else { checkUnused = FALSE; } status = VspCheckDiffAreaFileLocation(diffAreaFilter, h, appInfoOffset, firstDiffAreaBlockOffset, locationOffset, initialBitmapOffset, checkUnused); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_FAILURE_TO_OPEN_CRITICAL_FILE, status, 5, FALSE); ZwClose(h); ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); ObDereferenceObject(extension->DeviceObject); break; } VspAcquire(Filter->Root); if (extension->IsDead || !extension->DiffAreaFile) { VspRelease(Filter->Root); ZwClose(h); ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); ObDereferenceObject(extension->DeviceObject); continue; } ASSERT(!extension->DiffAreaFile->FileHandle); extension->DiffAreaFile->FileHandle = h; VspRelease(Filter->Root); ObDereferenceObject(diffAreaFilter->TargetObject); ObDereferenceObject(diffAreaFilter->DeviceObject); ObDereferenceObject(extension->DeviceObject); } return status; } VOID VspCallDriver( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; ASSERT(context->Type == VSP_CONTEXT_TYPE_COPY_EXTENTS); IoCallDriver(context->CopyExtents.Extension->Filter->DeviceObject, context->CopyExtents.Irp); } DECLSPEC_NOINLINE VOID VspClearDirtyCrashdumpFlag( IN PVSP_CONTROL_ITEM_SNAPSHOT SnapshotControlItem ) { SnapshotControlItem->SnapshotControlItemFlags &= ~VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_CRASHDUMP; } VOID VspMarkCrashdumpClean( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->CopyExtents.Extension; PFILTER_EXTENSION filter = extension->Filter; BOOLEAN hiberfile = context->CopyExtents.HiberfileIncluded; BOOLEAN pagefile = context->CopyExtents.PagefileIncluded; UCHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem; NTSTATUS status; LONG hibernate; ASSERT(context->Type == VSP_CONTEXT_TYPE_COPY_EXTENTS); VspFreeContext(filter->Root, context); VspAcquire(filter->Root); if (IsListEmpty(&filter->VolumeList)) { VspRelease(filter->Root); if (hiberfile) { InterlockedExchange(&filter->HibernatePending, FALSE); } ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); return; } if (!extension->IsPersistent) { VspRelease(filter->Root); ASSERT(!pagefile); if (hiberfile) { InterlockedExchange(&extension->HiberFileCopied, TRUE); hibernate = InterlockedExchange(&filter->HibernatePending, FALSE); if (hibernate) { IoRaiseInformationalHardError(STATUS_VOLSNAP_HIBERNATE_READY, NULL, NULL); } } ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); return; } VspAcquireNonPagedResource(filter, NULL, FALSE); status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &extension->SnapshotGuid, FALSE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(extension, filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 14, FALSE); } VspReleaseNonPagedResource(filter); VspRelease(filter->Root); VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); if (hiberfile) { InterlockedExchange(&filter->HibernatePending, FALSE); } ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); return; } snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; if (hiberfile) { snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_HIBERFIL_COPIED; InterlockedExchange(&extension->HiberFileCopied, TRUE); } if (pagefile) { VspClearDirtyCrashdumpFlag(snapshotControlItem); snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_PAGEFILE_COPIED; InterlockedExchange(&extension->PageFileCopied, TRUE); } status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &extension->SnapshotGuid, TRUE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(extension, filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 15, FALSE); } VspReleaseNonPagedResource(filter); VspRelease(filter->Root); VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); if (hiberfile) { InterlockedExchange(&filter->HibernatePending, FALSE); } ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); return; } VspReleaseNonPagedResource(filter); VspRelease(filter->Root); if (hiberfile) { hibernate = InterlockedExchange(&filter->HibernatePending, FALSE); if (hibernate) { IoRaiseInformationalHardError(STATUS_VOLSNAP_HIBERNATE_READY, NULL, NULL); } } ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); } NTSTATUS VspCopyExtentsCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->CopyExtents.Extension; PFILTER_EXTENSION filter = extension->Filter; PIO_STACK_LOCATION nextSp; PDIFF_AREA_FILE_ALLOCATION fileExtent; LONGLONG offset; ULONG length; ASSERT(context->Type == VSP_CONTEXT_TYPE_COPY_EXTENTS); ASSERT(!IsListEmpty(&context->CopyExtents.ExtentList)); fileExtent = CONTAINING_RECORD(context->CopyExtents.ExtentList.Flink, DIFF_AREA_FILE_ALLOCATION, ListEntry); ASSERT(fileExtent->NLength >= 0); if (!fileExtent->NLength) { RemoveEntryList(&fileExtent->ListEntry); ExFreePool(fileExtent); if (IsListEmpty(&context->CopyExtents.ExtentList)) { ExInitializeWorkItem(&context->WorkItem, VspMarkCrashdumpClean, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } fileExtent = CONTAINING_RECORD(context->CopyExtents.ExtentList.Flink, DIFF_AREA_FILE_ALLOCATION, ListEntry); ASSERT(fileExtent->NLength > 0); } length = 0x80000; // 512 KB offset = fileExtent->Offset; if (length > fileExtent->NLength) { length = (ULONG) fileExtent->NLength; } fileExtent->Offset += length; fileExtent->NLength -= length; Irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp = IoGetNextIrpStackLocation(Irp); nextSp->Parameters.Write.ByteOffset.QuadPart = offset; nextSp->Parameters.Write.Length = length; nextSp->MajorFunction = IRP_MJ_WRITE; nextSp->DeviceObject = filter->DeviceObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(Irp, VspCopyExtentsCompletionRoutine, context, TRUE, TRUE, TRUE); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspCopyExtents( IN PVOLUME_EXTENSION Extension, IN PLIST_ENTRY ExtentList, IN BOOLEAN HiberfileIncluded, IN BOOLEAN PagefileIncluded ) { PVSP_CONTEXT context; PIRP irp; PIO_STACK_LOCATION nextSp; if (IsListEmpty(ExtentList)) { if (HiberfileIncluded) { InterlockedExchange(&Extension->Filter->HibernatePending, FALSE); } return STATUS_SUCCESS; } context = VspAllocateContext(Extension->Root); if (!context) { if (HiberfileIncluded) { InterlockedExchange(&Extension->Filter->HibernatePending, FALSE); } return STATUS_INSUFFICIENT_RESOURCES; } irp = IoAllocateIrp((CCHAR) Extension->Root->StackSize, FALSE); if (!irp) { if (HiberfileIncluded) { InterlockedExchange(&Extension->Filter->HibernatePending, FALSE); } VspFreeContext(Extension->Root, context); return STATUS_INSUFFICIENT_RESOURCES; } irp->MdlAddress = NULL; context->Type = VSP_CONTEXT_TYPE_COPY_EXTENTS; ExInitializeWorkItem(&context->WorkItem, VspCallDriver, context); context->CopyExtents.Extension = Extension; context->CopyExtents.Irp = irp; context->CopyExtents.ExtentList = *ExtentList; context->CopyExtents.ExtentList.Flink->Blink = &context->CopyExtents.ExtentList; context->CopyExtents.ExtentList.Blink->Flink = &context->CopyExtents.ExtentList; context->CopyExtents.HiberfileIncluded = HiberfileIncluded; context->CopyExtents.PagefileIncluded = PagefileIncluded; ObReferenceObject(Extension->DeviceObject); ObReferenceObject(Extension->Filter->DeviceObject); VspCopyExtentsCompletionRoutine(NULL, irp, context); return STATUS_SUCCESS; } VOID VspSystemSid( IN OUT SID* Sid ) { Sid->Revision = SID_REVISION; Sid->SubAuthorityCount = 1; Sid->IdentifierAuthority = ntAuthority; Sid->SubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID; } VOID VspAdminSid( IN OUT SID* Sid ) { Sid->Revision = SID_REVISION; Sid->SubAuthorityCount = 2; Sid->IdentifierAuthority = ntAuthority; Sid->SubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID; Sid->SubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS; } BOOLEAN VspHasStrongAcl( IN HANDLE Handle, OUT PNTSTATUS Status ) { NTSTATUS status; ULONG sdLength; PSECURITY_DESCRIPTOR sd; PSID sid; BOOLEAN ownerDefaulted, daclPresent, daclDefaulted; PACL acl; ULONG i; PACCESS_ALLOWED_ACE ace; PSID systemSid; UCHAR sidBuffer[2*sizeof(SID)]; PSID adminSid; UCHAR sidBuffer2[2*sizeof(SID)]; ULONG priviledgedBits; status = ZwQuerySecurityObject(Handle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, NULL, 0, &sdLength); if (status != STATUS_BUFFER_TOO_SMALL) { // The file system does not support security. return TRUE; } sd = ExAllocatePoolWithTag(PagedPool, sdLength, VOLSNAP_TAG_SHORT_TERM); if (!sd) { *Status = STATUS_INSUFFICIENT_RESOURCES; return FALSE; } status = ZwQuerySecurityObject(Handle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, sd, sdLength, &sdLength); if (!NT_SUCCESS(status)) { ExFreePool(sd); *Status = status; return FALSE; } status = RtlGetDaclSecurityDescriptor(sd, &daclPresent, &acl, &daclDefaulted); if (!NT_SUCCESS(status)) { ExFreePool(sd); *Status = status; return FALSE; } status = RtlGetOwnerSecurityDescriptor(sd, &sid, &ownerDefaulted); if (!NT_SUCCESS(status)) { ExFreePool(sd); *Status = status; return FALSE; } systemSid = (PSID) sidBuffer; adminSid = (PSID) sidBuffer2; VspSystemSid((SID*) systemSid); VspAdminSid((SID*) adminSid); if (!sid) { ExFreePool(sd); *Status = STATUS_NO_SECURITY_ON_OBJECT; return FALSE; } if (!RtlEqualSid(sid, adminSid) && !RtlEqualSid(sid, systemSid)) { ExFreePool(sd); *Status = STATUS_NO_SECURITY_ON_OBJECT; return FALSE; } if (!daclPresent || (daclPresent && !acl)) { ExFreePool(sd); *Status = STATUS_NO_SECURITY_ON_OBJECT; return FALSE; } priviledgedBits = FILE_READ_DATA | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY | GENERIC_ALL | GENERIC_READ; for (i = 0; ; i++) { status = RtlGetAce(acl, i, (PVOID*) &ace); if (!NT_SUCCESS(status)) { ace = NULL; } if (!ace) { break; } if (ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) { continue; } sid = (PSID) &ace->SidStart; if (RtlEqualSid(sid, systemSid) || RtlEqualSid(sid, adminSid)) { continue; } if (ace->Mask&priviledgedBits) { ExFreePool(sd); *Status = STATUS_NO_SECURITY_ON_OBJECT; return FALSE; } } ExFreePool(sd); return TRUE; } VOID VspAdjustBitmap( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; NTSTATUS status; LIST_ENTRY extentList; WCHAR nameBuffer[100]; UNICODE_STRING name; OBJECT_ATTRIBUTES oa; HANDLE h; IO_STATUS_BLOCK ioStatus; KIRQL irql; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; BOOLEAN isNtfs; PWORK_QUEUE_ITEM workItem; PWORKER_THREAD_ROUTINE workerRoutine; PVOID parameter; PVSP_DIFF_AREA_FILE diffAreaFile; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(extension->Root, context); KeWaitForSingleObject(&extension->Root->PastBootReinit, Executive, KernelMode, FALSE, NULL); if (extension->IsDetected) { status = VspOpenFilesAndValidateSnapshots(extension->Filter); if (!NT_SUCCESS(status)) { if (!extension->Filter->ControlBlockFileHandle) { VspAcquireNonPagedResource(extension, NULL, FALSE); VspCreateStartBlock(extension->Filter, 0, extension->Filter->MaximumVolumeSpace); extension->Filter->FirstControlBlockVolumeOffset = 0; VspReleaseNonPagedResource(extension); } VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, TRUE); goto Finish; } } else { if (extension->IsPersistent) { VspComputeIgnorableBitmap(extension, NULL); } KeSetEvent(&extension->Filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); swprintf(nameBuffer, L"\\Device\\HarddiskVolumeShadowCopy%d\\pagefile.sys", extension->VolumeNumber); RtlInitUnicodeString(&name, nameBuffer); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(status)) { if (!VspHasStrongAcl(h, &status)) { ZwClose(h); } } if (NT_SUCCESS(status)) { VspMarkFileAllocationInBitmap(extension, h, NULL, NULL); if (extension->ContainsCrashdumpFile) { ASSERT(extension->IsPersistent); status = VspQueryListOfExtents(h, 0, &extentList, NULL, TRUE); if (NT_SUCCESS(status)) { status = VspCopyExtents(extension, &extentList, FALSE, TRUE); if (!NT_SUCCESS(status)) { while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } VspLogError(NULL, extension->Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 4, FALSE); VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); } } else { VspLogError(NULL, extension->Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 1, FALSE); VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); } } ZwClose(h); } else if (extension->ContainsCrashdumpFile) { VspLogError(NULL, extension->Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 2, FALSE); VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); } swprintf(nameBuffer, L"\\Device\\HarddiskVolumeShadowCopy%d\\hiberfil.sys", extension->VolumeNumber); RtlInitUnicodeString(&name, nameBuffer); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(status)) { if (!VspHasStrongAcl(h, &status)) { ZwClose(h); } } if (NT_SUCCESS(status)) { VspMarkFileAllocationInBitmap(extension, h, NULL, NULL); if (extension->IsPersistent) { status = STATUS_SUCCESS; isNtfs = TRUE; } else { status = VspIsNtfs(h, &isNtfs); } if (NT_SUCCESS(status) && isNtfs) { status = VspQueryListOfExtents(h, 0, &extentList, NULL, TRUE); if (NT_SUCCESS(status)) { status = VspCopyExtents(extension, &extentList, TRUE, FALSE); if (!NT_SUCCESS(status)) { while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } VspLogError(NULL, extension->Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 14, FALSE); VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); } } else { VspLogError(NULL, extension->Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 3, FALSE); VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); } } ZwClose(h); } } status = VspComputeIgnorableProduct(extension); KeAcquireSpinLock(&extension->SpinLock, &irql); if (extension->IgnorableProduct) { if (NT_SUCCESS(status) && extension->VolumeBlockBitmap) { VspOrBitmaps(extension->VolumeBlockBitmap, extension->IgnorableProduct); } else if (extension->VolumeBlockBitmap && extension->IsDetected) { KeReleaseSpinLock(&extension->SpinLock, irql); if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_FAILED_FREE_SPACE_DETECTION, status, 1, FALSE); } VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, TRUE); goto Finish; } ExFreePool(extension->IgnorableProduct->Buffer); ExFreePool(extension->IgnorableProduct); extension->IgnorableProduct = NULL; } KeReleaseSpinLock(&extension->SpinLock, irql); if (extension->IsDetected) { InterlockedExchange(&extension->OkToGrowDiffArea, TRUE); if (extension->DetectedNeedForGrow) { context = VspAllocateContext(extension->Root); if (context) { KeAcquireSpinLock(&extension->SpinLock, &irql); ASSERT(!extension->GrowDiffAreaFilePending); ASSERT(IsListEmpty(&extension->WaitingForDiffAreaSpace)); extension->PastFileSystemOperations = FALSE; extension->GrowDiffAreaFilePending = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); context->Type = VSP_CONTEXT_TYPE_GROW_DIFF_AREA; ExInitializeWorkItem(&context->WorkItem, VspGrowDiffArea, context); context->GrowDiffArea.Extension = extension; ObReferenceObject(extension->DeviceObject); VspQueueWorkItem(extension->Root, &context->WorkItem, 0); } } } Finish: if (extension->IsDetected) { KeSetEvent(&extension->Filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); } VspAcquire(extension->Root); if (extension->IgnoreCopyDataReference) { extension->IgnoreCopyDataReference = FALSE; InterlockedDecrement(&extension->Filter->IgnoreCopyData); } VspRelease(extension->Root); KeAcquireSpinLock(&extension->Root->ESpinLock, &irql); ASSERT(extension->Root->AdjustBitmapInProgress); if (IsListEmpty(&extension->Root->AdjustBitmapQueue)) { extension->Root->AdjustBitmapInProgress = FALSE; KeReleaseSpinLock(&extension->Root->ESpinLock, irql); } else { l = RemoveHeadList(&extension->Root->AdjustBitmapQueue); KeReleaseSpinLock(&extension->Root->ESpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); workerRoutine = workItem->WorkerRoutine; parameter = workItem->Parameter; ExInitializeWorkItem(workItem, workerRoutine, parameter); ExQueueWorkItem(workItem, DelayedWorkQueue); } ObDereferenceObject(extension->Filter->TargetObject); ObDereferenceObject(extension->Filter->DeviceObject); ObDereferenceObject(extension->DeviceObject); } VOID VspSetIgnorableBlocksInBitmapWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; NTSTATUS status; KIRQL irql; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); if (!extension->IsDetected) { status = VspMarkFreeSpaceInBitmap(extension, NULL, NULL); if (NT_SUCCESS(status)) { InterlockedExchange(&extension->OkToGrowDiffArea, TRUE); } else { if (!extension->Filter->DestroyAllSnapshotsPending) { VspLogError(extension, NULL, VS_ABORT_SNAPSHOTS_FAILED_FREE_SPACE_DETECTION, status, 2, FALSE); } VspDestroyAllSnapshots(extension->Filter, NULL, FALSE, FALSE); } } ExInitializeWorkItem(&context->WorkItem, VspAdjustBitmap, context); KeAcquireSpinLock(&extension->Root->ESpinLock, &irql); if (extension->Root->AdjustBitmapInProgress) { InsertTailList(&extension->Root->AdjustBitmapQueue, &context->WorkItem.List); KeReleaseSpinLock(&extension->Root->ESpinLock, irql); } else { extension->Root->AdjustBitmapInProgress = TRUE; KeReleaseSpinLock(&extension->Root->ESpinLock, irql); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } } VOID VspFreeCopyIrp( IN PVOLUME_EXTENSION Extension, IN PIRP CopyIrp ) { KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry; if (Extension->EmergencyCopyIrp == CopyIrp) { KeAcquireSpinLock(&Extension->SpinLock, &irql); if (IsListEmpty(&Extension->EmergencyCopyIrpQueue)) { Extension->EmergencyCopyIrpInUse = FALSE; KeReleaseSpinLock(&Extension->SpinLock, irql); return; } l = RemoveHeadList(&Extension->EmergencyCopyIrpQueue); KeReleaseSpinLock(&Extension->SpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) workItem->Parameter; tableEntry->CopyIrp = CopyIrp; workItem->WorkerRoutine(workItem->Parameter); return; } if (!Extension->EmergencyCopyIrpInUse) { ExFreePool(MmGetMdlVirtualAddress(CopyIrp->MdlAddress)); IoFreeMdl(CopyIrp->MdlAddress); IoFreeIrp(CopyIrp); return; } KeAcquireSpinLock(&Extension->SpinLock, &irql); if (IsListEmpty(&Extension->EmergencyCopyIrpQueue)) { KeReleaseSpinLock(&Extension->SpinLock, irql); ExFreePool(MmGetMdlVirtualAddress(CopyIrp->MdlAddress)); IoFreeMdl(CopyIrp->MdlAddress); IoFreeIrp(CopyIrp); return; } l = RemoveHeadList(&Extension->EmergencyCopyIrpQueue); KeReleaseSpinLock(&Extension->SpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) workItem->Parameter; tableEntry->CopyIrp = CopyIrp; workItem->WorkerRoutine(workItem->Parameter); } VOID VspApplyThresholdDelta( IN PVOLUME_EXTENSION Extension, IN ULONG IncreaseDelta ) { PVSP_DIFF_AREA_FILE diffAreaFile; PVSP_CONTEXT context; KIRQL irql; ASSERT(Extension->DiffAreaFile); diffAreaFile = Extension->DiffAreaFile; if (diffAreaFile->NextAvailable + Extension->DiffAreaFileIncrease <= diffAreaFile->AllocatedFileSize) { return; } if (diffAreaFile->NextAvailable + Extension->DiffAreaFileIncrease - IncreaseDelta > diffAreaFile->AllocatedFileSize) { return; } context = VspAllocateContext(Extension->Root); if (!context) { if (!Extension->OkToGrowDiffArea) { VspLogError(Extension, diffAreaFile->Filter, VS_GROW_BEFORE_FREE_SPACE, STATUS_SUCCESS, 3, FALSE); } return; } KeAcquireSpinLock(&Extension->SpinLock, &irql); ASSERT(!Extension->GrowDiffAreaFilePending); ASSERT(IsListEmpty(&Extension->WaitingForDiffAreaSpace)); Extension->PastFileSystemOperations = FALSE; Extension->GrowDiffAreaFilePending = TRUE; KeReleaseSpinLock(&Extension->SpinLock, irql); context->Type = VSP_CONTEXT_TYPE_GROW_DIFF_AREA; ExInitializeWorkItem(&context->WorkItem, VspGrowDiffArea, context); context->GrowDiffArea.Extension = Extension; ObReferenceObject(Extension->DeviceObject); if (!Extension->OkToGrowDiffArea) { ObReferenceObject(Extension->Filter->DeviceObject); ExInitializeWorkItem(&context->WorkItem, VspWaitForInstall, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); return; } VspQueueWorkItem(Extension->Root, &context->WorkItem, 0); } NTSTATUS VspCheckOnDiskNotCommitted( IN PVOLUME_EXTENSION Extension ) { PFILTER_EXTENSION filter = Extension->Filter; NTSTATUS status; UCHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem; if (!Extension->OnDiskNotCommitted) { return STATUS_SUCCESS; } Extension->OnDiskNotCommitted = FALSE; status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, FALSE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(Extension, filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 16, FALSE); } VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); return status; } snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; snapshotControlItem->SnapshotOrderNumber = Extension->SnapshotOrderNumber; snapshotControlItem->SnapshotTime = Extension->CommitTimeStamp; if (Extension->ContainsCrashdumpFile) { snapshotControlItem->SnapshotControlItemFlags = VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_CRASHDUMP; } if (Extension->NoDiffAreaFill) { snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_NO_DIFF_AREA_FILL; } status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, TRUE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { if (!filter->DestroyAllSnapshotsPending) { VspLogError(Extension, filter, VS_ABORT_SNAPSHOTS_IO_FAILURE, status, 17, FALSE); } VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); return status; } return STATUS_SUCCESS; } VOID VspWriteVolume( IN PVOID Context ) /*++ Routine Description: This routine performs a volume write, making sure that all of the parts of the volume write have an old version of the data placed in the diff area for the snapshot. Arguments: Irp - Supplies the I/O request packet. Return Value: None. --*/ { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->WriteVolume.Extension; PIRP irp = (PIRP) context->WriteVolume.Irp; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp); PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp); PFILTER_EXTENSION filter = extension->Filter; NTSTATUS status; PLIST_ENTRY l; LONGLONG start, end, roundedStart, roundedEnd; ULONG irpLength, increase, increaseDelta; TEMP_TRANSLATION_TABLE_ENTRY keyTableEntry; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry; PVOID nodeOrParent; TABLE_SEARCH_RESULT searchResult; KIRQL irql; CCHAR stackSize; PVSP_DIFF_AREA_FILE diffAreaFile; PDO_EXTENSION rootExtension; PVOID buffer; PMDL mdl; ASSERT(context->Type == VSP_CONTEXT_TYPE_WRITE_VOLUME); if (extension->OnDiskNotCommitted) { status = VspCheckOnDiskNotCommitted(extension); if (!NT_SUCCESS(status)) { VspFreeContext(filter->Root, context); VspReleaseNonPagedResource(extension); VspDecrementIrpRefCount(irp); return; } } start = irpSp->Parameters.Read.ByteOffset.QuadPart; irpLength = irpSp->Parameters.Read.Length; end = start + irpLength; if (context->WriteVolume.RoundedStart) { roundedStart = context->WriteVolume.RoundedStart; } else { roundedStart = start&(~(BLOCK_SIZE - 1)); } roundedEnd = end&(~(BLOCK_SIZE - 1)); if (roundedEnd != end) { roundedEnd += BLOCK_SIZE; } ASSERT(extension->VolumeBlockBitmap); for (; roundedStart < roundedEnd; roundedStart += BLOCK_SIZE) { if (roundedStart < 0 || roundedStart >= extension->VolumeSize) { continue; } KeAcquireSpinLock(&extension->SpinLock, &irql); if (RtlCheckBit(extension->VolumeBlockBitmap, (ULONG) (roundedStart>>BLOCK_SHIFT))) { KeReleaseSpinLock(&extension->SpinLock, irql); continue; } KeReleaseSpinLock(&extension->SpinLock, irql); keyTableEntry.VolumeOffset = roundedStart; tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTableFull( &extension->TempVolumeBlockTable, &keyTableEntry, &nodeOrParent, &searchResult); if (tableEntry) { context = VspAllocateContext(extension->Root); if (context) { context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = extension; context->Extension.Irp = irp; } else { context = (PVSP_CONTEXT) Context; ASSERT(context->Type == VSP_CONTEXT_TYPE_WRITE_VOLUME); context->WriteVolume.RoundedStart = roundedStart; } ExInitializeWorkItem(&context->WorkItem, VspDecrementIrpRefCountWorker, context); KeAcquireSpinLock(&extension->SpinLock, &irql); if (tableEntry->IsComplete) { KeReleaseSpinLock(&extension->SpinLock, irql); if (context->Type == VSP_CONTEXT_TYPE_EXTENSION) { VspFreeContext(extension->Root, context); } continue; } InterlockedIncrement((PLONG) &nextSp->Parameters.Write.Length); InsertTailList(&tableEntry->WaitingQueueDpc, &context->WorkItem.List); KeReleaseSpinLock(&extension->SpinLock, irql); if (context == Context) { VspReleaseNonPagedResource(extension); return; } continue; } RtlZeroMemory(&keyTableEntry, sizeof(TEMP_TRANSLATION_TABLE_ENTRY)); keyTableEntry.VolumeOffset = roundedStart; ASSERT(!extension->TempTableEntry); extension->TempTableEntry = VspAllocateTempTableEntry(extension->Root); if (!extension->TempTableEntry) { rootExtension = extension->Root; KeAcquireSpinLock(&rootExtension->ESpinLock, &irql); if (rootExtension->EmergencyTableEntryInUse) { context = (PVSP_CONTEXT) Context; ASSERT(context->Type == VSP_CONTEXT_TYPE_WRITE_VOLUME); context->WriteVolume.RoundedStart = roundedStart; InsertTailList(&rootExtension->WorkItemWaitingList, &context->WorkItem.List); if (!rootExtension->WorkItemWaitingListNeedsChecking) { InterlockedExchange( &rootExtension->WorkItemWaitingListNeedsChecking, TRUE); } KeReleaseSpinLock(&rootExtension->ESpinLock, irql); VspReleaseNonPagedResource(extension); return; } rootExtension->EmergencyTableEntryInUse = TRUE; KeReleaseSpinLock(&rootExtension->ESpinLock, irql); extension->TempTableEntry = rootExtension->EmergencyTableEntry; } tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlInsertElementGenericTableFull( &extension->TempVolumeBlockTable, &keyTableEntry, sizeof(TEMP_TRANSLATION_TABLE_ENTRY), NULL, nodeOrParent, searchResult); ASSERT(tableEntry); if (extension->TempVolumeBlockTable.NumberGenericTableElements > extension->MaximumNumberOfTempEntries) { extension->MaximumNumberOfTempEntries = extension->TempVolumeBlockTable.NumberGenericTableElements; VspQueryDiffAreaFileIncrease(extension, &increase); ASSERT(increase >= extension->DiffAreaFileIncrease); increaseDelta = increase - extension->DiffAreaFileIncrease; if (increaseDelta) { InterlockedExchange((PLONG) &extension->DiffAreaFileIncrease, (LONG) increase); VspApplyThresholdDelta(extension, increaseDelta); } } tableEntry->Extension = extension; tableEntry->WriteIrp = irp; diffAreaFile = extension->DiffAreaFile; ASSERT(diffAreaFile); tableEntry->TargetObject = diffAreaFile->Filter->TargetObject; tableEntry->IsComplete = FALSE; InitializeListHead(&tableEntry->WaitingQueueDpc); tableEntry->CopyIrp = IoAllocateIrp( (CCHAR) extension->Root->StackSize, FALSE); buffer = ExAllocatePoolWithTagPriority(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER, LowPoolPriority); mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!tableEntry->CopyIrp || !buffer || !mdl) { if (tableEntry->CopyIrp) { IoFreeIrp(tableEntry->CopyIrp); tableEntry->CopyIrp = NULL; } if (buffer) { ExFreePool(buffer); } if (mdl) { IoFreeMdl(mdl); } KeAcquireSpinLock(&extension->SpinLock, &irql); if (extension->EmergencyCopyIrpInUse) { InterlockedIncrement((PLONG) &nextSp->Parameters.Write.Length); ExInitializeWorkItem(&tableEntry->WorkItem, VspWriteVolumePhase1, tableEntry); InsertTailList(&extension->EmergencyCopyIrpQueue, &tableEntry->WorkItem.List); KeReleaseSpinLock(&extension->SpinLock, irql); continue; } extension->EmergencyCopyIrpInUse = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); tableEntry->CopyIrp = extension->EmergencyCopyIrp; } else { MmBuildMdlForNonPagedPool(mdl); tableEntry->CopyIrp->MdlAddress = mdl; } InterlockedIncrement((PLONG) &nextSp->Parameters.Write.Length); VspAllocateDiffAreaSpace(extension, &tableEntry->TargetOffset, &tableEntry->FileOffset, NULL, NULL); VspWriteVolumePhase1(tableEntry); } context = (PVSP_CONTEXT) Context; VspFreeContext(filter->Root, context); VspReleaseNonPagedResource(extension); VspDecrementIrpRefCount(irp); } VOID VspIrpsTimerDpc( IN PKDPC TimerDpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) Context; KIRQL irql; LIST_ENTRY q; PLIST_ENTRY l; BOOLEAN emptyQueue; if (TimerDpc) { VspLogError(NULL, filter, VS_FLUSH_AND_HOLD_IRP_TIMEOUT, STATUS_SUCCESS, 0, FALSE); } IoStopTimer(filter->DeviceObject); KeAcquireSpinLock(&filter->SpinLock, &irql); ASSERT(filter->ExternalWaiter); filter->ExternalWaiter = FALSE; InterlockedIncrement(&filter->RefCount); InterlockedDecrement(&filter->HoldIncomingWrites); if (filter->HoldIncomingWrites) { KeReleaseSpinLock(&filter->SpinLock, irql); VspDecrementRefCount(filter); return; } KeResetEvent(&filter->ZeroRefEvent); if (IsListEmpty(&filter->HoldQueue)) { emptyQueue = FALSE; } else { emptyQueue = TRUE; q = filter->HoldQueue; InitializeListHead(&filter->HoldQueue); } KeReleaseSpinLock(&filter->SpinLock, irql); if (emptyQueue) { q.Blink->Flink = &q; q.Flink->Blink = &q; VspEmptyIrpQueue(filter->Root->DriverObject, &q); } } VOID VspEndCommitDpc( IN PKDPC TimerDpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) Context; VspLogError(NULL, filter, VS_END_COMMIT_TIMEOUT, STATUS_CANCELLED, 0, FALSE); InterlockedExchange(&filter->HibernatePending, FALSE); KeSetEvent(&filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); ObDereferenceObject(filter->DeviceObject); } NTSTATUS VspCheckForMemoryPressure( IN PFILTER_EXTENSION Filter ) /*++ Routine Description: This routine will allocate 256 K of paged and non paged pool. If these allocs succeed, it indicates that the system is not under memory pressure and so it is ok to hold write irps for the next second. Arguments: Filter - Supplies the filter extension. Return Value: NTSTATUS --*/ { PVOID p, np; p = ExAllocatePoolWithTagPriority(PagedPool, MEMORY_PRESSURE_CHECK_ALLOC_SIZE, VOLSNAP_TAG_SHORT_TERM, LowPoolPriority); if (!p) { VspLogError(NULL, Filter, VS_MEMORY_PRESSURE_DURING_LOVELACE, STATUS_INSUFFICIENT_RESOURCES, 1, FALSE); return STATUS_INSUFFICIENT_RESOURCES; } np = ExAllocatePoolWithTagPriority(NonPagedPool, MEMORY_PRESSURE_CHECK_ALLOC_SIZE, VOLSNAP_TAG_SHORT_TERM, LowPoolPriority); if (!np) { ExFreePool(p); VspLogError(NULL, Filter, VS_MEMORY_PRESSURE_DURING_LOVELACE, STATUS_INSUFFICIENT_RESOURCES, 2, FALSE); return STATUS_INSUFFICIENT_RESOURCES; } ExFreePool(np); ExFreePool(p); return STATUS_SUCCESS; } VOID VspOneSecondTimerWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID WorkItem ) { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIO_WORKITEM workItem = (PIO_WORKITEM) WorkItem; NTSTATUS status; status = VspCheckForMemoryPressure(filter); if (!NT_SUCCESS(status)) { InterlockedExchange(&filter->LastReleaseDueToMemoryPressure, TRUE); VspReleaseWrites(filter); } IoFreeWorkItem(workItem); } VOID VspOneSecondTimer( IN PDEVICE_OBJECT DeviceObject, IN PVOID Filter ) /*++ Routine Description: This routine will get called once every second after an IoStartTimer. This routine checks for memory pressure and aborts the lovelace operation if any memory pressure is detected. Arguments: DeviceObject - Supplies the device object. Filter - Supplies the filter extension. Return Value: None. --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) Filter; PIO_WORKITEM workItem; workItem = IoAllocateWorkItem(filter->DeviceObject); if (!workItem) { VspLogError(NULL, filter, VS_MEMORY_PRESSURE_DURING_LOVELACE, STATUS_INSUFFICIENT_RESOURCES, 3, FALSE); VspReleaseWrites(filter); return; } IoQueueWorkItem(workItem, VspOneSecondTimerWorker, CriticalWorkQueue, workItem); } NTSTATUS VolSnapAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine creates and initializes a new FILTER for the corresponding volume PDO. Arguments: DriverObject - Supplies the VOLSNAP driver object. PhysicalDeviceObject - Supplies the volume PDO. Return Value: NTSTATUS --*/ { NTSTATUS status; PDEVICE_OBJECT deviceObject; PDO_EXTENSION rootExtension; PFILTER_EXTENSION filter; status = IoCreateDevice(DriverObject, sizeof(FILTER_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { return status; } rootExtension = (PDO_EXTENSION) IoGetDriverObjectExtension(DriverObject, VolSnapAddDevice); if (!rootExtension) { IoDeleteDevice(deviceObject); return STATUS_NO_SUCH_DEVICE; } filter = (PFILTER_EXTENSION) deviceObject->DeviceExtension; RtlZeroMemory(filter, sizeof(FILTER_EXTENSION)); filter->DeviceObject = deviceObject; filter->Root = rootExtension; filter->DeviceExtensionType = DEVICE_EXTENSION_FILTER; KeInitializeSpinLock(&filter->SpinLock); filter->TargetObject = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); if (!filter->TargetObject) { IoDeleteDevice(deviceObject); return STATUS_NO_SUCH_DEVICE; } filter->Pdo = PhysicalDeviceObject; KeInitializeSemaphore(&filter->ControlBlockFileHandleSemaphore, 1, 1); InitializeListHead(&filter->SnapshotLookupTableEntries); InitializeListHead(&filter->DiffAreaLookupTableEntries); KeInitializeEvent(&filter->ControlBlockFileHandleReady, NotificationEvent, TRUE); filter->RefCount = 1; InitializeListHead(&filter->HoldQueue); KeInitializeEvent(&filter->ZeroRefEvent, NotificationEvent, FALSE); KeInitializeSemaphore(&filter->ZeroRefSemaphore, 1, 1); KeInitializeTimer(&filter->HoldWritesTimer); KeInitializeDpc(&filter->HoldWritesTimerDpc, VspIrpsTimerDpc, filter); KeInitializeEvent(&filter->EndCommitProcessCompleted, NotificationEvent, TRUE); InitializeListHead(&filter->VolumeList); KeInitializeSemaphore(&filter->CriticalOperationSemaphore, 1, 1); InitializeListHead(&filter->DeadVolumeList); InitializeListHead(&filter->DiffAreaFilesOnThisFilter); InitializeListHead(&filter->CopyOnWriteList); filter->DiffAreaVolume = filter; KeInitializeTimer(&filter->EndCommitTimer); KeInitializeDpc(&filter->EndCommitTimerDpc, VspEndCommitDpc, filter); InitializeListHead(&filter->NonPagedResourceList); InitializeListHead(&filter->PagedResourceList); filter->EpicNumber = 1; status = IoInitializeTimer(deviceObject, VspOneSecondTimer, filter); if (!NT_SUCCESS(status)) { IoDetachDevice(filter->TargetObject); IoDeleteDevice(deviceObject); return status; } deviceObject->DeviceType = filter->TargetObject->DeviceType; deviceObject->Flags |= DO_DIRECT_IO; if (filter->TargetObject->Flags & DO_POWER_PAGABLE) { deviceObject->Flags |= DO_POWER_PAGABLE; } if (filter->TargetObject->Flags & DO_POWER_INRUSH) { deviceObject->Flags |= DO_POWER_INRUSH; } deviceObject->Characteristics |= (filter->Pdo->Characteristics&FILE_CHARACTERISTICS_PROPAGATED); VspAcquire(filter->Root); if (filter->TargetObject->StackSize > filter->Root->StackSize) { InterlockedExchange(&filter->Root->StackSize, (LONG) filter->TargetObject->StackSize); } InsertTailList(&filter->Root->FilterList, &filter->ListEntry); VspRelease(filter->Root); deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } NTSTATUS VolSnapCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_CREATE. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request block. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID VspReadSnapshotPhase3( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->ReadSnapshot.Extension; PIRP irp = context->ReadSnapshot.OriginalReadIrp; PFILTER_EXTENSION filter = extension->Filter; KIRQL irql; PVOLUME_EXTENSION e; TRANSLATION_TABLE_ENTRY keyTableEntry; NTSTATUS status; PTRANSLATION_TABLE_ENTRY tableEntry; TEMP_TRANSLATION_TABLE_ENTRY keyTempTableEntry; PTEMP_TRANSLATION_TABLE_ENTRY tempTableEntry; ASSERT(context->Type == VSP_CONTEXT_TYPE_READ_SNAPSHOT); KeAcquireSpinLock(&filter->SpinLock, &irql); e = CONTAINING_RECORD(filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); KeReleaseSpinLock(&filter->SpinLock, irql); keyTableEntry.VolumeOffset = context->ReadSnapshot.OriginalVolumeOffset; status = STATUS_SUCCESS; _try { tableEntry = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable(&e->VolumeBlockTable, &keyTableEntry); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); tableEntry = NULL; } if (!NT_SUCCESS(status)) { irp->IoStatus.Status = status; irp->IoStatus.Information = 0; VspReleasePagedResource(extension); VspFreeContext(extension->Root, context); VspDecrementVolumeIrpRefCount(irp); return; } if (tableEntry && tableEntry->TargetObject != context->ReadSnapshot.TargetObject) { context->ReadSnapshot.TargetObject = tableEntry->TargetObject; if (tableEntry->Flags&VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY) { context->ReadSnapshot.IsCopyTarget = TRUE; } else { context->ReadSnapshot.IsCopyTarget = FALSE; } context->ReadSnapshot.TargetOffset = tableEntry->TargetOffset; VspReleasePagedResource(extension); VspReadSnapshotPhase1(context); return; } VspAcquireNonPagedResource(e, NULL, FALSE); keyTempTableEntry.VolumeOffset = context->ReadSnapshot.TargetOffset; tempTableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable( &e->TempVolumeBlockTable, &keyTempTableEntry); if (tempTableEntry) { KeAcquireSpinLock(&e->SpinLock, &irql); if (!tempTableEntry->IsComplete) { ExInitializeWorkItem(&context->WorkItem, VspReadSnapshotPhase1, context); InsertTailList(&tempTableEntry->WaitingQueueDpc, &context->WorkItem.List); KeReleaseSpinLock(&e->SpinLock, irql); VspReleaseNonPagedResource(e); VspReleasePagedResource(extension); return; } KeReleaseSpinLock(&e->SpinLock, irql); context->ReadSnapshot.TargetObject = tempTableEntry->TargetObject; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = tempTableEntry->TargetOffset; VspReleaseNonPagedResource(e); VspReleasePagedResource(e); VspReadSnapshotPhase1(context); return; } VspReleaseNonPagedResource(e); VspReleasePagedResource(extension); VspFreeContext(extension->Root, context); VspDecrementVolumeIrpRefCount(irp); } NTSTATUS VspReadSnapshotPhase2( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->ReadSnapshot.Extension; PIRP irp = context->ReadSnapshot.OriginalReadIrp; ASSERT(context->Type == VSP_CONTEXT_TYPE_READ_SNAPSHOT); if (!NT_SUCCESS(Irp->IoStatus.Status)) { irp->IoStatus = Irp->IoStatus; } IoFreeMdl(Irp->MdlAddress); IoFreeIrp(Irp); if (context->ReadSnapshot.IsCopyTarget) { ExInitializeWorkItem(&context->WorkItem, VspReadSnapshotPhase3, context); VspAcquirePagedResource(extension, &context->WorkItem); return STATUS_MORE_PROCESSING_REQUIRED; } VspFreeContext(extension->Root, context); VspDecrementVolumeIrpRefCount(irp); return STATUS_MORE_PROCESSING_REQUIRED; } VOID VspReadSnapshotPhase1( IN PVOID Context ) /*++ Routine Description: This routine is called when a read snapshot routine is waiting for somebody else to finish updating the public area of a table entry. When this routine is called, the public area of the table entry is valid. Arguments: Context - Supplies the context. Return Value: None. --*/ { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->ReadSnapshot.Extension; TEMP_TRANSLATION_TABLE_ENTRY keyTempTableEntry; PTEMP_TRANSLATION_TABLE_ENTRY tempTableEntry; TRANSLATION_TABLE_ENTRY keyTableEntry; PTRANSLATION_TABLE_ENTRY tableEntry; NTSTATUS status; PIRP irp; PIO_STACK_LOCATION nextSp; PCHAR vp; PMDL mdl; ASSERT(context->Type == VSP_CONTEXT_TYPE_READ_SNAPSHOT); if (!context->ReadSnapshot.TargetObject || !extension->IsStarted) { irp = context->ReadSnapshot.OriginalReadIrp; irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; irp->IoStatus.Information = 0; VspFreeContext(extension->Root, context); VspDecrementVolumeIrpRefCount(irp); return; } irp = IoAllocateIrp(context->ReadSnapshot.TargetObject->StackSize, FALSE); if (!irp) { irp = context->ReadSnapshot.OriginalReadIrp; irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; irp->IoStatus.Information = 0; VspFreeContext(extension->Root, context); VspDecrementVolumeIrpRefCount(irp); return; } vp = (PCHAR) MmGetMdlVirtualAddress( context->ReadSnapshot.OriginalReadIrp->MdlAddress) + context->ReadSnapshot.OriginalReadIrpOffset; mdl = IoAllocateMdl(vp, context->ReadSnapshot.Length, FALSE, FALSE, NULL); if (!mdl) { IoFreeIrp(irp); irp = context->ReadSnapshot.OriginalReadIrp; irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; irp->IoStatus.Information = 0; VspFreeContext(extension->Root, context); VspDecrementVolumeIrpRefCount(irp); return; } IoBuildPartialMdl(context->ReadSnapshot.OriginalReadIrp->MdlAddress, mdl, vp, context->ReadSnapshot.Length); irp->MdlAddress = mdl; irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp = IoGetNextIrpStackLocation(irp); nextSp->Parameters.Read.ByteOffset.QuadPart = context->ReadSnapshot.TargetOffset + context->ReadSnapshot.BlockOffset; nextSp->Parameters.Read.Length = context->ReadSnapshot.Length; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = context->ReadSnapshot.TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspReadSnapshotPhase2, context, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); } VOID VspReadSnapshot( IN PVOID Context ) /*++ Routine Description: This routine kicks off a read snapshot. First the table is searched to see if any of the data for this IRP resides in the diff area. If not, then the Irp is sent directly to the original volume and then the diff area is checked again when it returns to fill in any gaps that may have been written while the IRP was in transit. Arguments: Irp - Supplies the I/O request packet. Return Value: None. --*/ { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; PIRP irp = context->Extension.Irp; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp); PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp); PFILTER_EXTENSION filter = extension->Filter; LONGLONG start, end, roundedStart, roundedEnd; ULONG irpOffset, irpLength, length, blockOffset; TRANSLATION_TABLE_ENTRY keyTableEntry; TEMP_TRANSLATION_TABLE_ENTRY keyTempTableEntry; PVOLUME_EXTENSION e; PTRANSLATION_TABLE_ENTRY tableEntry; PTEMP_TRANSLATION_TABLE_ENTRY tempTableEntry; KIRQL irql; NTSTATUS status; LONGLONG copyTarget; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(extension->Root, context); if (!extension->IsStarted) { irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; irp->IoStatus.Information = 0; VspReleasePagedResource(extension); VspDecrementVolumeIrpRefCount(irp); return; } start = irpSp->Parameters.Read.ByteOffset.QuadPart; irpLength = irpSp->Parameters.Read.Length; end = start + irpLength; roundedStart = start&(~(BLOCK_SIZE - 1)); roundedEnd = end&(~(BLOCK_SIZE - 1)); if (roundedEnd != end) { roundedEnd += BLOCK_SIZE; } irpOffset = 0; RtlZeroMemory(&keyTableEntry, sizeof(keyTableEntry)); for (; roundedStart < roundedEnd; roundedStart += BLOCK_SIZE) { if (roundedStart < start) { blockOffset = (ULONG) (start - roundedStart); } else { blockOffset = 0; } copyTarget = 0; length = BLOCK_SIZE - blockOffset; if (irpLength < length) { length = irpLength; } keyTableEntry.VolumeOffset = roundedStart; e = extension; status = STATUS_SUCCESS; for (;;) { _try { tableEntry = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable(&e->VolumeBlockTable, &keyTableEntry); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); tableEntry = NULL; } if (tableEntry) { if (!(tableEntry->Flags& VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY)) { break; } copyTarget = tableEntry->TargetOffset; keyTableEntry.VolumeOffset = copyTarget; tableEntry = NULL; } if (!NT_SUCCESS(status)) { irp->IoStatus.Status = status; irp->IoStatus.Information = 0; break; } KeAcquireSpinLock(&filter->SpinLock, &irql); if (e->ListEntry.Flink == &filter->VolumeList) { KeReleaseSpinLock(&filter->SpinLock, irql); break; } e = CONTAINING_RECORD(e->ListEntry.Flink, VOLUME_EXTENSION, ListEntry); KeReleaseSpinLock(&filter->SpinLock, irql); } if (!tableEntry) { if (!NT_SUCCESS(status)) { break; } if (copyTarget) { keyTempTableEntry.VolumeOffset = copyTarget; } else { keyTempTableEntry.VolumeOffset = roundedStart; } VspAcquireNonPagedResource(e, NULL, FALSE); tempTableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable( &e->TempVolumeBlockTable, &keyTempTableEntry); if (!tempTableEntry && !copyTarget) { VspReleaseNonPagedResource(e); irpOffset += length; irpLength -= length; continue; } } context = VspAllocateContext(extension->Root); if (!context) { if (!tableEntry) { VspReleaseNonPagedResource(e); } irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; irp->IoStatus.Information = 0; break; } context->Type = VSP_CONTEXT_TYPE_READ_SNAPSHOT; context->ReadSnapshot.Extension = extension; context->ReadSnapshot.OriginalReadIrp = irp; context->ReadSnapshot.OriginalReadIrpOffset = irpOffset; context->ReadSnapshot.OriginalVolumeOffset = roundedStart; context->ReadSnapshot.BlockOffset = blockOffset; context->ReadSnapshot.Length = length; context->ReadSnapshot.TargetObject = NULL; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = 0; if (!tableEntry) { if (copyTarget) { VspReleaseNonPagedResource(e); context->ReadSnapshot.TargetObject = filter->TargetObject; context->ReadSnapshot.IsCopyTarget = TRUE; context->ReadSnapshot.TargetOffset = copyTarget; InterlockedIncrement((PLONG) &nextSp->Parameters.Read.Length); VspReadSnapshotPhase1(context); irpOffset += length; irpLength -= length; continue; } KeAcquireSpinLock(&e->SpinLock, &irql); if (!tempTableEntry->IsComplete) { InterlockedIncrement((PLONG) &nextSp->Parameters.Read.Length); ExInitializeWorkItem(&context->WorkItem, VspReadSnapshotPhase1, context); InsertTailList(&tempTableEntry->WaitingQueueDpc, &context->WorkItem.List); KeReleaseSpinLock(&e->SpinLock, irql); VspReleaseNonPagedResource(e); irpOffset += length; irpLength -= length; continue; } KeReleaseSpinLock(&e->SpinLock, irql); context->ReadSnapshot.TargetObject = tempTableEntry->TargetObject; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = tempTableEntry->TargetOffset; VspReleaseNonPagedResource(e); InterlockedIncrement((PLONG) &nextSp->Parameters.Read.Length); VspReadSnapshotPhase1(context); irpOffset += length; irpLength -= length; continue; } context->ReadSnapshot.TargetObject = tableEntry->TargetObject; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = tableEntry->TargetOffset; InterlockedIncrement((PLONG) &nextSp->Parameters.Read.Length); VspReadSnapshotPhase1(context); irpOffset += length; irpLength -= length; } VspReleasePagedResource(extension); VspDecrementVolumeIrpRefCount(irp); } NTSTATUS VspReadCompletionForReadSnapshot( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Extension ) /*++ Routine Description: This routine is the completion to a read of the filter in response to a snapshot read. This completion routine queues a worker routine to look at the diff area table and fill in any parts of the original that have been invalidated. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the I/O request packet. Extension - Supplies the volume extension. Return Value: NTSTATUS --*/ { PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) Extension; PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp); PVSP_CONTEXT context; nextSp->Parameters.Read.Length = 1; // Use this for a ref count. if (!NT_SUCCESS(Irp->IoStatus.Status)) { VspDecrementVolumeIrpRefCount(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } context = VspAllocateContext(extension->Root); if (!context) { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Information = 0; VspDecrementVolumeIrpRefCount(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = extension; context->Extension.Irp = Irp; ExInitializeWorkItem(&context->WorkItem, VspReadSnapshot, context); VspAcquirePagedResource(extension, &context->WorkItem); return STATUS_MORE_PROCESSING_REQUIRED; } RTL_GENERIC_COMPARE_RESULTS VspTableCompareRoutine( IN PRTL_GENERIC_TABLE Table, IN PVOID First, IN PVOID Second ) { PTRANSLATION_TABLE_ENTRY first = (PTRANSLATION_TABLE_ENTRY) First; PTRANSLATION_TABLE_ENTRY second = (PTRANSLATION_TABLE_ENTRY) Second; if (first->VolumeOffset < second->VolumeOffset) { return GenericLessThan; } else if (first->VolumeOffset > second->VolumeOffset) { return GenericGreaterThan; } return GenericEqual; } VOID VspCreateHeap( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; PIRP irp = context->Extension.Irp; ULONG increase; OBJECT_ATTRIBUTES oa; NTSTATUS status; SIZE_T size; LARGE_INTEGER sectionSize, sectionOffset; HANDLE h; PVOID mapPointer; KIRQL irql; BOOLEAN emptyQueue; LIST_ENTRY q; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(extension->Root, context); // First check that there is 5 MB of space available. This driver // should not consume all available page file space. InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); size = 5*1024*1024; sectionSize.QuadPart = size; status = ZwCreateSection(&h, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE, &oa, §ionSize, PAGE_READWRITE, SEC_COMMIT, NULL); if (!NT_SUCCESS(status)) { VspLogError(extension, NULL, VS_CANT_CREATE_HEAP, status, 1, FALSE); goto Finish; } sectionOffset.QuadPart = 0; mapPointer = NULL; status = ZwMapViewOfSection(h, NtCurrentProcess(), &mapPointer, 0, 0, §ionOffset, &size, ViewShare, 0, PAGE_READWRITE); ZwClose(h); if (!NT_SUCCESS(status)) { VspLogError(extension, NULL, VS_CANT_CREATE_HEAP, status, 2, FALSE); goto Finish; } status = ZwUnmapViewOfSection(NtCurrentProcess(), mapPointer); ASSERT(NT_SUCCESS(status)); // Now we have 5 MB, so go ahead and make the alloc. increase = extension->DiffAreaFileIncrease; increase >>= BLOCK_SHIFT; size = increase*(sizeof(TRANSLATION_TABLE_ENTRY) + sizeof(RTL_BALANCED_LINKS)); size = (size + 0xFFFF)&(~0xFFFF); ASSERT(size >= MINIMUM_TABLE_HEAP_SIZE); if (extension->RootSemaphoreHeld) { irp = (PIRP) 1; } DoOver: sectionSize.QuadPart = size; status = ZwCreateSection(&h, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE, &oa, §ionSize, PAGE_READWRITE, SEC_COMMIT, NULL); if (!NT_SUCCESS(status)) { if (size > MINIMUM_TABLE_HEAP_SIZE) { size = MINIMUM_TABLE_HEAP_SIZE; goto DoOver; } VspLogError(extension, NULL, VS_CANT_CREATE_HEAP, status, 3, FALSE); goto Finish; } sectionOffset.QuadPart = 0; mapPointer = NULL; status = ZwMapViewOfSection(h, NtCurrentProcess(), &mapPointer, 0, 0, §ionOffset, &size, ViewShare, 0, PAGE_READWRITE); ZwClose(h); if (!NT_SUCCESS(status)) { if (size > MINIMUM_TABLE_HEAP_SIZE) { size = MINIMUM_TABLE_HEAP_SIZE; goto DoOver; } VspLogError(extension, NULL, VS_CANT_CREATE_HEAP, status, 4, FALSE); goto Finish; } status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { status = ZwUnmapViewOfSection(NtCurrentProcess(), mapPointer); ASSERT(NT_SUCCESS(status)); goto Finish; } VspAcquirePagedResource(extension, NULL); extension->NextDiffAreaFileMap = mapPointer; extension->NextDiffAreaFileMapSize = (ULONG) size; extension->DiffAreaFileMapProcess = NtCurrentProcess(); VspReleasePagedResource(extension); VspDecrementVolumeRefCount(extension); Finish: KeAcquireSpinLock(&extension->SpinLock, &irql); if (extension->PageFileSpaceCreatePending) { extension->PageFileSpaceCreatePending = FALSE; if (IsListEmpty(&extension->WaitingForPageFileSpace)) { emptyQueue = FALSE; } else { emptyQueue = TRUE; q = extension->WaitingForPageFileSpace; InitializeListHead(&extension->WaitingForPageFileSpace); } } else { emptyQueue = FALSE; } KeReleaseSpinLock(&extension->SpinLock, irql); if (emptyQueue) { q.Flink->Blink = &q; q.Blink->Flink = &q; while (!IsListEmpty(&q)) { l = RemoveHeadList(&q); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); VspAcquirePagedResource(extension, workItem); } } ObDereferenceObject(extension->DeviceObject); } PVOID VspTableAllocateRoutine( IN PRTL_GENERIC_TABLE Table, IN CLONG Size ) { PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) Table->TableContext; PVOID p; POLD_HEAP_ENTRY oldHeap; PVSP_CONTEXT context; KIRQL irql; if (extension->NextAvailable + Size <= extension->DiffAreaFileMapSize) { p = (PCHAR) extension->DiffAreaFileMap + extension->NextAvailable; extension->NextAvailable += Size; return p; } if (!extension->NextDiffAreaFileMap) { return NULL; } oldHeap = (POLD_HEAP_ENTRY) ExAllocatePoolWithTag(PagedPool, sizeof(OLD_HEAP_ENTRY), VOLSNAP_TAG_OLD_HEAP); if (!oldHeap) { return NULL; } context = VspAllocateContext(extension->Root); if (!context) { ExFreePool(oldHeap); return NULL; } KeAcquireSpinLock(&extension->SpinLock, &irql); ASSERT(!extension->PageFileSpaceCreatePending); ASSERT(IsListEmpty(&extension->WaitingForPageFileSpace)); extension->PageFileSpaceCreatePending = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); oldHeap->DiffAreaFileMap = extension->DiffAreaFileMap; InsertTailList(&extension->OldHeaps, &oldHeap->ListEntry); extension->DiffAreaFileMap = extension->NextDiffAreaFileMap; extension->DiffAreaFileMapSize = extension->NextDiffAreaFileMapSize; extension->NextAvailable = 0; extension->NextDiffAreaFileMap = NULL; context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = extension; context->Extension.Irp = NULL; ObReferenceObject(extension->DeviceObject); ExInitializeWorkItem(&context->WorkItem, VspCreateHeap, context); if (extension->RootSemaphoreHeld) { ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } else { VspQueueWorkItem(extension->Root, &context->WorkItem, 0); } p = extension->DiffAreaFileMap; extension->NextAvailable += Size; return p; } VOID VspTableFreeRoutine( IN PRTL_GENERIC_TABLE Table, IN PVOID Buffer ) { } PVOID VspTempTableAllocateRoutine( IN PRTL_GENERIC_TABLE Table, IN CLONG Size ) { PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) Table->TableContext; PVOID r; ASSERT(Size <= sizeof(RTL_BALANCED_LINKS) + sizeof(TEMP_TRANSLATION_TABLE_ENTRY)); r = extension->TempTableEntry; extension->TempTableEntry = NULL; return r; } VOID VspTempTableFreeRoutine( IN PRTL_GENERIC_TABLE Table, IN PVOID Buffer ) { PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) Table->TableContext; VspFreeTempTableEntry(extension->Root, Buffer); } PFILTER_EXTENSION VspFindFilter( IN PDO_EXTENSION RootExtension, IN PFILTER_EXTENSION Filter, IN PUNICODE_STRING VolumeName, IN PFILE_OBJECT FileObject ) { NTSTATUS status; PDEVICE_OBJECT deviceObject, d; PLIST_ENTRY l; PFILTER_EXTENSION filter; if (VolumeName) { status = IoGetDeviceObjectPointer(VolumeName, FILE_READ_ATTRIBUTES, &FileObject, &deviceObject); if (!NT_SUCCESS(status)) { if (Filter) { VspLogError(NULL, Filter, VS_FAILURE_ADDING_DIFF_AREA, status, 1, FALSE); } return NULL; } } deviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); for (l = RootExtension->FilterList.Flink; l != &RootExtension->FilterList; l = l->Flink) { filter = CONTAINING_RECORD(l, FILTER_EXTENSION, ListEntry); d = IoGetAttachedDeviceReference(filter->DeviceObject); ObDereferenceObject(d); if (d == deviceObject) { break; } } ObDereferenceObject(deviceObject); if (VolumeName) { ObDereferenceObject(FileObject); } if (l != &RootExtension->FilterList) { return filter; } if (Filter) { VspLogError(NULL, Filter, VS_FAILURE_ADDING_DIFF_AREA, STATUS_NOT_FOUND, 2, FALSE); } return NULL; } NTSTATUS VspIsNtfs( IN HANDLE FileHandle, OUT PBOOLEAN IsNtfs ) { ULONG size; PFILE_FS_ATTRIBUTE_INFORMATION fsAttributeInfo; NTSTATUS status; IO_STATUS_BLOCK ioStatus; size = FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) + 4*sizeof(WCHAR); fsAttributeInfo = (PFILE_FS_ATTRIBUTE_INFORMATION) ExAllocatePoolWithTag(PagedPool, size, VOLSNAP_TAG_SHORT_TERM); if (!fsAttributeInfo) { return STATUS_INSUFFICIENT_RESOURCES; } status = ZwQueryVolumeInformationFile(FileHandle, &ioStatus, fsAttributeInfo, size, FileFsAttributeInformation); if (status == STATUS_BUFFER_OVERFLOW) { *IsNtfs = FALSE; ExFreePool(fsAttributeInfo); return STATUS_SUCCESS; } if (!NT_SUCCESS(status)) { ExFreePool(fsAttributeInfo); return status; } if (fsAttributeInfo->FileSystemNameLength == 8 && fsAttributeInfo->FileSystemName[0] == 'N' && fsAttributeInfo->FileSystemName[1] == 'T' && fsAttributeInfo->FileSystemName[2] == 'F' && fsAttributeInfo->FileSystemName[3] == 'S') { ExFreePool(fsAttributeInfo); *IsNtfs = TRUE; return STATUS_SUCCESS; } ExFreePool(fsAttributeInfo); *IsNtfs = FALSE; return STATUS_SUCCESS; } LONGLONG VspQueryVolumeSize( IN PFILTER_EXTENSION Filter ) { PDEVICE_OBJECT targetObject; KEVENT event; PIRP irp; GET_LENGTH_INFORMATION lengthInfo; IO_STATUS_BLOCK ioStatus; NTSTATUS status; targetObject = Filter->TargetObject; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_LENGTH_INFO, targetObject, NULL, 0, &lengthInfo, sizeof(lengthInfo), FALSE, &event, &ioStatus); if (!irp) { return 0; } status = IoCallDriver(targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return 0; } return lengthInfo.Length.QuadPart; } VOID VspCancelRoutine( IN OUT PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) /*++ Routine Description: This routine is called on when the given IRP is cancelled. It will dequeue this IRP off the work queue and complete the request as CANCELLED. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IRP. Return Value: None. --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; ASSERT(Irp == filter->FlushAndHoldIrp); filter->FlushAndHoldIrp = NULL; RemoveEntryList(&Irp->Tail.Overlay.ListEntry); IoReleaseCancelSpinLock(Irp->CancelIrql); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } VOID VspFsTimerDpc( IN PKDPC TimerDpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PDO_EXTENSION rootExtension = (PDO_EXTENSION) Context; KIRQL irql; PIRP irp; PFILTER_EXTENSION filter; IoAcquireCancelSpinLock(&irql); while (!IsListEmpty(&rootExtension->HoldIrps)) { irp = CONTAINING_RECORD(rootExtension->HoldIrps.Flink, IRP, Tail.Overlay.ListEntry); irp->CancelIrql = irql; IoSetCancelRoutine(irp, NULL); filter = (PFILTER_EXTENSION) IoGetCurrentIrpStackLocation(irp)-> DeviceObject->DeviceExtension; ObReferenceObject(filter->DeviceObject); VspCancelRoutine(filter->DeviceObject, irp); VspLogError(NULL, filter, VS_FLUSH_AND_HOLD_FS_TIMEOUT, STATUS_CANCELLED, 0, FALSE); ObDereferenceObject(filter->DeviceObject); IoAcquireCancelSpinLock(&irql); } rootExtension->HoldRefCount = 0; IoReleaseCancelSpinLock(irql); } VOID VspZeroRefCallback( IN PFILTER_EXTENSION Filter ) { PIRP irp = (PIRP) Filter->ZeroRefContext; LARGE_INTEGER timeout; timeout.QuadPart = -10*1000*1000*((LONGLONG) Filter->HoldWritesTimeout); KeSetTimer(&Filter->HoldWritesTimer, timeout, &Filter->HoldWritesTimerDpc); irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_SOUND_INCREMENT); } VOID VspFlushAndHoldWriteIrps( IN PIRP Irp, IN ULONG HoldWritesTimeout ) /*++ Routine Description: This routine waits for outstanding write requests to complete while holding incoming write requests. This IRP will complete when all outstanding IRPs have completed. A timer will be set for the given timeout value and held writes irps will be released after that point or when IOCTL_VOLSNAP_RELEASE_WRITES comes in, whichever is sooner. Arguments: Irp - Supplies the I/O request packet. HoldWritesTimeout - Supplies the maximum length of time in seconds that a write IRP will be held up. Return Value: None. --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PFILTER_EXTENSION filter = (PFILTER_EXTENSION) irpSp->DeviceObject->DeviceExtension; KIRQL irql; NTSTATUS status; KeAcquireSpinLock(&filter->SpinLock, &irql); if (filter->ExternalWaiter) { KeReleaseSpinLock(&filter->SpinLock, irql); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } filter->ExternalWaiter = TRUE; filter->ZeroRefCallback = VspZeroRefCallback; filter->ZeroRefContext = Irp; filter->HoldWritesTimeout = HoldWritesTimeout; if (filter->HoldIncomingWrites) { InterlockedIncrement(&filter->HoldIncomingWrites); InterlockedIncrement(&filter->RefCount); } else { InterlockedIncrement(&filter->HoldIncomingWrites); } KeReleaseSpinLock(&filter->SpinLock, irql); VspDecrementRefCount(filter); IoStartTimer(filter->DeviceObject); status = VspCheckForMemoryPressure(filter); if (!NT_SUCCESS(status)) { InterlockedExchange(&filter->LastReleaseDueToMemoryPressure, TRUE); VspReleaseWrites(filter); } } NTSTATUS VspFlushAndHoldWrites( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine is called for multiple volumes at once. On the first call the GUID is checked and if it is different than the current one then the current set is aborted. If the GUID is new then subsequent calls are compared to the GUID passed in here until the required number of calls is completed. A timer is used to wait until all of the IRPs have reached this driver and then another time out is used after all of these calls complete to wait for IOCTL_VOLSNAP_RELEASE_WRITES to be sent to all of the volumes involved. Arguments: Filter - Supplies the filter device extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PDO_EXTENSION rootExtension = Filter->Root; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_FLUSH_AND_HOLD_INPUT input; KIRQL irql; LARGE_INTEGER timeout; LIST_ENTRY q; PLIST_ENTRY l; PIRP irp; PFILTER_EXTENSION filter; ULONG irpTimeout; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLSNAP_FLUSH_AND_HOLD_INPUT)) { return STATUS_INVALID_PARAMETER; } input = (PVOLSNAP_FLUSH_AND_HOLD_INPUT) Irp->AssociatedIrp.SystemBuffer; if (!input->NumberOfVolumesToFlush || !input->SecondsToHoldFileSystemsTimeout || !input->SecondsToHoldIrpsTimeout) { return STATUS_INVALID_PARAMETER; } IoAcquireCancelSpinLock(&irql); if (Filter->FlushAndHoldIrp) { IoReleaseCancelSpinLock(irql); VspLogError(NULL, Filter, VS_TWO_FLUSH_AND_HOLDS, STATUS_INVALID_PARAMETER, 1, FALSE); return STATUS_INVALID_PARAMETER; } if (rootExtension->HoldRefCount) { if (!IsEqualGUID(rootExtension->HoldInstanceGuid, input->InstanceId)) { IoReleaseCancelSpinLock(irql); VspLogError(NULL, Filter, VS_TWO_FLUSH_AND_HOLDS, STATUS_INVALID_PARAMETER, 2, FALSE); return STATUS_INVALID_PARAMETER; } } else { if (IsEqualGUID(rootExtension->HoldInstanceGuid, input->InstanceId)) { IoReleaseCancelSpinLock(irql); return STATUS_INVALID_PARAMETER; } rootExtension->HoldRefCount = input->NumberOfVolumesToFlush + 1; rootExtension->HoldInstanceGuid = input->InstanceId; rootExtension->SecondsToHoldFsTimeout = input->SecondsToHoldFileSystemsTimeout; rootExtension->SecondsToHoldIrpTimeout = input->SecondsToHoldIrpsTimeout; timeout.QuadPart = -10*1000*1000* ((LONGLONG) rootExtension->SecondsToHoldFsTimeout); KeSetTimer(&rootExtension->HoldTimer, timeout, &rootExtension->HoldTimerDpc); } Filter->FlushAndHoldIrp = Irp; InsertTailList(&rootExtension->HoldIrps, &Irp->Tail.Overlay.ListEntry); IoSetCancelRoutine(Irp, VspCancelRoutine); IoMarkIrpPending(Irp); rootExtension->HoldRefCount--; if (rootExtension->HoldRefCount != 1) { IoReleaseCancelSpinLock(irql); return STATUS_PENDING; } InitializeListHead(&q); while (!IsListEmpty(&rootExtension->HoldIrps)) { l = RemoveHeadList(&rootExtension->HoldIrps); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); filter = (PFILTER_EXTENSION) IoGetCurrentIrpStackLocation(irp)->DeviceObject-> DeviceExtension; InsertTailList(&q, l); filter->FlushAndHoldIrp = NULL; IoSetCancelRoutine(irp, NULL); } irpTimeout = rootExtension->SecondsToHoldIrpTimeout; if (KeCancelTimer(&rootExtension->HoldTimer)) { IoReleaseCancelSpinLock(irql); VspFsTimerDpc(&rootExtension->HoldTimerDpc, rootExtension->HoldTimerDpc.DeferredContext, rootExtension->HoldTimerDpc.SystemArgument1, rootExtension->HoldTimerDpc.SystemArgument2); } else { IoReleaseCancelSpinLock(irql); } while (!IsListEmpty(&q)) { l = RemoveHeadList(&q); irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry); VspFlushAndHoldWriteIrps(irp, irpTimeout); } return STATUS_PENDING; } NTSTATUS VspCreateDiffAreaFileName( IN PDEVICE_OBJECT DeviceObject, IN PVOLUME_EXTENSION Extension, OUT PUNICODE_STRING DiffAreaFileName, IN BOOLEAN ValidateSystemVolumeInformationFolder, IN GUID* SnapshotGuid ) /*++ Routine Description: This routine builds the diff area file name for the given diff area volume and the given volume snapshot number. The name formed will look like \. Arguments: Filter - Supplies the filter extension. VolumeSnapshotNumber - Supplies the volume snapshot number. DiffAreaFileName - Returns the name of the diff area file. Return Value: NTSTATUS --*/ { PDEVICE_OBJECT targetObject = DeviceObject; KEVENT event; PMOUNTDEV_NAME name; UCHAR buffer[512]; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; UNICODE_STRING sysvol, guidString, numberString, string; WCHAR numberBuffer[80]; KeInitializeEvent(&event, NotificationEvent, FALSE); name = (PMOUNTDEV_NAME) buffer; irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, targetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return status; } RtlInitUnicodeString(&sysvol, RTL_SYSTEM_VOLUME_INFORMATION_FOLDER); if (SnapshotGuid) { status = RtlStringFromGUID(*SnapshotGuid, &guidString); if (!NT_SUCCESS(status)) { return status; } swprintf(numberBuffer, L"\\%s", guidString.Buffer); ExFreePool(guidString.Buffer); } else if (Extension) { if (Extension->IsPersistent) { status = RtlStringFromGUID(Extension->SnapshotGuid, &guidString); if (!NT_SUCCESS(status)) { return status; } swprintf(numberBuffer, L"\\%s", guidString.Buffer); ExFreePool(guidString.Buffer); } else { swprintf(numberBuffer, L"\\%d", Extension->VolumeNumber); } } else { swprintf(numberBuffer, L"\\*"); } RtlInitUnicodeString(&numberString, numberBuffer); status = RtlStringFromGUID(VSP_DIFF_AREA_FILE_GUID, &guidString); if (!NT_SUCCESS(status)) { return status; } string.MaximumLength = name->NameLength + sizeof(WCHAR) + sysvol.Length + numberString.Length + guidString.Length + sizeof(WCHAR); string.Length = 0; string.Buffer = (PWCHAR) ExAllocatePoolWithTag(PagedPool, string.MaximumLength, VOLSNAP_TAG_SHORT_TERM); if (!string.Buffer) { ExFreePool(guidString.Buffer); return STATUS_INSUFFICIENT_RESOURCES; } string.Length = name->NameLength; RtlCopyMemory(string.Buffer, name->Name, string.Length); string.Buffer[string.Length/sizeof(WCHAR)] = '\\'; string.Length += sizeof(WCHAR); if (ValidateSystemVolumeInformationFolder) { RtlCreateSystemVolumeInformationFolder(&string); } RtlAppendUnicodeStringToString(&string, &sysvol); RtlAppendUnicodeStringToString(&string, &numberString); RtlAppendUnicodeStringToString(&string, &guidString); ExFreePool(guidString.Buffer); string.Buffer[string.Length/sizeof(WCHAR)] = 0; *DiffAreaFileName = string; return STATUS_SUCCESS; } NTSTATUS VspCreateSecurityDescriptor( OUT PSECURITY_DESCRIPTOR* SecurityDescriptor, OUT PACL* Acl ) { PSECURITY_DESCRIPTOR sd; NTSTATUS status; ULONG aclLength; PACL acl; sd = (PSECURITY_DESCRIPTOR) ExAllocatePoolWithTag(PagedPool, sizeof(SECURITY_DESCRIPTOR), VOLSNAP_TAG_SHORT_TERM); if (!sd) { return STATUS_INSUFFICIENT_RESOURCES; } status = RtlCreateSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); if (!NT_SUCCESS(status)) { ExFreePool(sd); return status; } aclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid) - sizeof(ULONG); acl = (PACL) ExAllocatePoolWithTag(PagedPool, aclLength, VOLSNAP_TAG_SHORT_TERM); if (!acl) { ExFreePool(sd); return STATUS_INSUFFICIENT_RESOURCES; } status = RtlCreateAcl(acl, aclLength, ACL_REVISION); if (!NT_SUCCESS(status)) { ExFreePool(acl); ExFreePool(sd); return status; } status = RtlAddAccessAllowedAce(acl, ACL_REVISION, DELETE, SeExports->SeAliasAdminsSid); if (!NT_SUCCESS(status)) { ExFreePool(acl); ExFreePool(sd); return status; } status = RtlSetDaclSecurityDescriptor(sd, TRUE, acl, FALSE); if (!NT_SUCCESS(status)) { ExFreePool(acl); ExFreePool(sd); return status; } *SecurityDescriptor = sd; *Acl = acl; return STATUS_SUCCESS; } NTSTATUS VspPinFile( IN PDEVICE_OBJECT TargetObject, IN HANDLE FileHandle ) /*++ Routine Description: This routine pins down the extents of the given diff area file so that defrag operations are disabled. Arguments: DiffAreaFile - Supplies the diff area file. Return Value: NTSTATUS --*/ { NTSTATUS status; UNICODE_STRING volumeName; OBJECT_ATTRIBUTES oa; HANDLE h; IO_STATUS_BLOCK ioStatus; MARK_HANDLE_INFO markHandleInfo; status = VspCreateDiffAreaFileName(TargetObject, NULL, &volumeName, FALSE, NULL); if (!NT_SUCCESS(status)) { return status; } // 66 characters back to take of \System Volume Information\*{guid} // resulting in the name of the volume. volumeName.Length -= 66*sizeof(WCHAR); volumeName.Buffer[volumeName.Length/sizeof(WCHAR)] = 0; InitializeObjectAttributes(&oa, &volumeName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); ExFreePool(volumeName.Buffer); if (!NT_SUCCESS(status)) { return status; } RtlZeroMemory(&markHandleInfo, sizeof(MARK_HANDLE_INFO)); markHandleInfo.VolumeHandle = h; markHandleInfo.HandleInfo = MARK_HANDLE_PROTECT_CLUSTERS; status = ZwFsControlFile(FileHandle, NULL, NULL, NULL, &ioStatus, FSCTL_MARK_HANDLE, &markHandleInfo, sizeof(markHandleInfo), NULL, 0); ZwClose(h); return status; } NTSTATUS VspOptimizeDiffAreaFileLocation( IN PFILTER_EXTENSION Filter, IN HANDLE FileHandle, IN PVOLUME_EXTENSION BitmapExtension, IN LONGLONG StartingOffset, IN LONGLONG FileSize ) /*++ Routine Description: This routine optimizes the location of the diff area file so that more of it can be used. Arguments: Filter - Supplies the filter extension where the diff area resides. FileHandle - Provides a handle to the diff area. BitmapExtension - Supplies the extension of the active snapshot on the given filter, if any. StartingOffset - Supplies the starting point of where to optimize the file. FileSize - Supplies the allocated size of the file. Return Value: NTSTATUS --*/ { NTSTATUS status; IO_STATUS_BLOCK ioStatus; FILE_FS_SIZE_INFORMATION fsSize; ULONG bitmapSize; PVOID bitmapBuffer; RTL_BITMAP bitmap; PMOUNTDEV_NAME mountdevName; UCHAR buffer[512]; KEVENT event; PIRP irp; UNICODE_STRING fileName; OBJECT_ATTRIBUTES oa; HANDLE h; KIRQL irql; ULONG numBitsToFind, bitIndex, bpc, bitsFound, chunk; MOVE_FILE_DATA moveFileData; // Align the given file and if 'BitmapExtension' is available, try to // confine the file to the bits already set in the bitmap in // 'BitmapExtension'. status = ZwQueryVolumeInformationFile(FileHandle, &ioStatus, &fsSize, sizeof(fsSize), FileFsSizeInformation); if (!NT_SUCCESS(status)) { return status; } bitmapSize = (ULONG) (fsSize.TotalAllocationUnits.QuadPart* fsSize.SectorsPerAllocationUnit* fsSize.BytesPerSector/BLOCK_SIZE); bitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(&bitmap, (PULONG) bitmapBuffer, bitmapSize); RtlClearAllBits(&bitmap); mountdevName = (PMOUNTDEV_NAME) buffer; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, Filter->TargetObject, NULL, 0, mountdevName, 500, FALSE, &event, &ioStatus); if (!irp) { ExFreePool(bitmapBuffer); return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(Filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { ExFreePool(bitmapBuffer); return status; } mountdevName->Name[mountdevName->NameLength/sizeof(WCHAR)] = 0; RtlInitUnicodeString(&fileName, mountdevName->Name); InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { ExFreePool(bitmapBuffer); return status; } status = VspMarkFreeSpaceInBitmap(NULL, h, &bitmap); if (!NT_SUCCESS(status)) { ZwClose(h); ExFreePool(bitmapBuffer); return status; } if (BitmapExtension) { status = VspIncrementVolumeRefCount(BitmapExtension); if (NT_SUCCESS(status)) { KeAcquireSpinLock(&BitmapExtension->SpinLock, &irql); if (BitmapExtension->VolumeBlockBitmap) { if (BitmapExtension->VolumeBlockBitmap->SizeOfBitMap < bitmap.SizeOfBitMap) { bitmap.SizeOfBitMap = BitmapExtension->VolumeBlockBitmap->SizeOfBitMap; } VspAndBitmaps(&bitmap, BitmapExtension->VolumeBlockBitmap); if (bitmap.SizeOfBitMap < bitmapSize) { bitmap.SizeOfBitMap = bitmapSize; RtlClearBits(&bitmap, BitmapExtension->VolumeBlockBitmap->SizeOfBitMap, bitmapSize - BitmapExtension->VolumeBlockBitmap->SizeOfBitMap); } } KeReleaseSpinLock(&BitmapExtension->SpinLock, irql); VspDecrementVolumeRefCount(BitmapExtension); } } numBitsToFind = (ULONG) ((FileSize - StartingOffset)/BLOCK_SIZE); bpc = fsSize.SectorsPerAllocationUnit*fsSize.BytesPerSector; chunk = 64; while (numBitsToFind) { bitsFound = numBitsToFind; if (bitsFound > chunk) { bitsFound = chunk; } bitIndex = RtlFindSetBits(&bitmap, bitsFound, 0); if (bitIndex == (ULONG) -1) { if (chunk == 1) { ZwClose(h); ExFreePool(bitmapBuffer); return STATUS_DISK_FULL; } chunk /= 2; continue; } moveFileData.FileHandle = FileHandle; moveFileData.StartingVcn.QuadPart = StartingOffset/bpc; moveFileData.StartingLcn.QuadPart = (((LONGLONG) bitIndex)<IgnoreCopyData); status = ZwFsControlFile(h, NULL, NULL, NULL, &ioStatus, FSCTL_MOVE_FILE, &moveFileData, sizeof(moveFileData), NULL, 0); InterlockedDecrement(&Filter->IgnoreCopyData); RtlClearBits(&bitmap, bitIndex, bitsFound); if (!NT_SUCCESS(status)) { continue; } numBitsToFind -= bitsFound; StartingOffset += ((LONGLONG) bitsFound)<AllocatedFileSize; status = VspCreateDiffAreaFileName(DiffAreaFile->Filter->TargetObject, DiffAreaFile->Extension, &diffAreaFileName, TRUE, NULL); if (!NT_SUCCESS(status)) { return status; } status = VspCreateSecurityDescriptor(&securityDescriptor, &acl); if (!NT_SUCCESS(status)) { ExFreePool(diffAreaFileName.Buffer); return status; } InitializeObjectAttributes(&oa, &diffAreaFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, securityDescriptor); status = ZwCreateFile(&DiffAreaFile->FileHandle, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &oa, &ioStatus, &diffAreaFileSize, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, 0, FILE_OVERWRITE_IF, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION, NULL, 0); ExFreePool(acl); ExFreePool(securityDescriptor); ExFreePool(diffAreaFileName.Buffer); if (NT_SUCCESS(status)) { status = VspSetFileSize(DiffAreaFile->FileHandle, diffAreaFileSize.QuadPart); if (!NT_SUCCESS(status)) { ZwClose(DiffAreaFile->FileHandle); DiffAreaFile->FileHandle = NULL; } } if (!NT_SUCCESS(status)) { if (status == STATUS_DISK_FULL) { if (!NoErrorLogOnDiskFull) { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_DIFF_AREA_CREATE_FAILED_LOW_DISK_SPACE, status, 0, FALSE); } } else { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_DIFF_AREA_CREATE_FAILED, status, 0, FALSE); } return status; } status = VspIsNtfs(DiffAreaFile->FileHandle, &isNtfs); if (!NT_SUCCESS(status) || !isNtfs) { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_NOT_NTFS, status, 0, FALSE); ZwClose(DiffAreaFile->FileHandle); DiffAreaFile->FileHandle = NULL; return STATUS_INVALID_PARAMETER; } VspAcquire(DiffAreaFile->Filter->Root); if (IsListEmpty(&DiffAreaFile->Filter->VolumeList)) { bitmapExtension = NULL; } else { bitmapExtension = CONTAINING_RECORD( DiffAreaFile->Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); if (bitmapExtension->IsDead) { bitmapExtension = NULL; } else { ObReferenceObject(bitmapExtension->DeviceObject); } } VspRelease(DiffAreaFile->Filter->Root); status = VspOptimizeDiffAreaFileLocation(DiffAreaFile->Filter, DiffAreaFile->FileHandle, bitmapExtension, 0, DiffAreaFile->AllocatedFileSize); if (!NT_SUCCESS(status)) { if (status != STATUS_DISK_FULL || !NoErrorLogOnDiskFull) { if (status == STATUS_DISK_FULL) { if (bitmapExtension) { if (bitmapExtension->IgnoreCopyDataReference) { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_INITIAL_DEFRAG_FAILED_BITMAP_ADJUSTMENT_IN_PROGRESS, status, 0, FALSE); } else { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_INITIAL_DEFRAG_FAILED, status, 0, FALSE); } } else { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_INITIAL_DEFRAG_FAILED_STRICT_FRAGMENTATION, status, 0, FALSE); } } else { VspLogError(DiffAreaFile->Extension, NULL, VS_CANT_ALLOCATE_BITMAP, status, 5, FALSE); } } ZwClose(DiffAreaFile->FileHandle); DiffAreaFile->FileHandle = NULL; if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } return status; } if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } status = VspPinFile(DiffAreaFile->Filter->TargetObject, DiffAreaFile->FileHandle); if (!NT_SUCCESS(status)) { VspLogError(DiffAreaFile->Extension, DiffAreaFile->Filter, VS_PIN_DIFF_AREA_FAILED, status, 0, FALSE); ZwClose(DiffAreaFile->FileHandle); DiffAreaFile->FileHandle = NULL; return status; } return STATUS_SUCCESS; } NTSTATUS VspTrimOlderSnapshotBeforeCreate( IN PFILTER_EXTENSION Filter, IN OUT PLONGLONG DiffAreaSpaceDeleted ) { LIST_ENTRY listOfDiffAreaFilesToClose; LIST_ENTRY listOfDeviceObjectToDelete; PVOLUME_EXTENSION extension; InitializeListHead(&listOfDiffAreaFilesToClose); InitializeListHead(&listOfDeviceObjectToDelete); VspAcquire(Filter->Root); if (IsListEmpty(&Filter->VolumeList)) { VspRelease(Filter->Root); return STATUS_UNSUCCESSFUL; } extension = CONTAINING_RECORD(Filter->VolumeList.Flink, VOLUME_EXTENSION, ListEntry); VspAcquireNonPagedResource(extension, NULL, FALSE); *DiffAreaSpaceDeleted += extension->DiffAreaFile->AllocatedFileSize; VspReleaseNonPagedResource(extension); VspLogError(extension, NULL, VS_DELETE_TO_TRIM_SPACE, STATUS_SUCCESS, 3, TRUE); VspDeleteOldestSnapshot(Filter, &listOfDiffAreaFilesToClose, &listOfDeviceObjectToDelete, FALSE, FALSE); VspRelease(Filter->Root); VspCloseDiffAreaFiles(&listOfDiffAreaFilesToClose, &listOfDeviceObjectToDelete); return STATUS_SUCCESS; } NTSTATUS VspCreateInitialDiffAreaFile( IN PVOLUME_EXTENSION Extension, IN LONGLONG InitialDiffAreaAllocation ) /*++ Routine Description: This routine creates the initial diff area file entries for the given device extension. Arguments: Extension - Supplies the volume extension. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = Extension->Filter; NTSTATUS status; PVSP_DIFF_AREA_FILE diffAreaFile; KIRQL irql; PVOID buf; PMDL mdl; LONGLONG diffAreaSpaceDeleted; VspAcquire(Extension->Root); if (!filter->DiffAreaVolume) { VspRelease(Extension->Root); return STATUS_INVALID_PARAMETER; } status = STATUS_SUCCESS; diffAreaFile = (PVSP_DIFF_AREA_FILE) ExAllocatePoolWithTag(NonPagedPool, sizeof(VSP_DIFF_AREA_FILE), VOLSNAP_TAG_DIFF_FILE); if (!diffAreaFile) { VspRelease(Extension->Root); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory(diffAreaFile, sizeof(VSP_DIFF_AREA_FILE)); diffAreaFile->Extension = Extension; diffAreaFile->Filter = filter->DiffAreaVolume; diffAreaFile->AllocatedFileSize = InitialDiffAreaAllocation; Extension->DiffAreaFile = diffAreaFile; InitializeListHead(&diffAreaFile->UnusedAllocationList); if (Extension->IsPersistent) { diffAreaFile->TableUpdateIrp = IoAllocateIrp((CCHAR) Extension->Root->StackSize, FALSE); if (!diffAreaFile->TableUpdateIrp) { Extension->DiffAreaFile = NULL; ExFreePool(diffAreaFile); VspRelease(Extension->Root); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } buf = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buf) { IoFreeIrp(diffAreaFile->TableUpdateIrp); Extension->DiffAreaFile = NULL; ExFreePool(diffAreaFile); VspRelease(Extension->Root); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory(buf, BLOCK_SIZE); mdl = IoAllocateMdl(buf, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); Extension->DiffAreaFile = NULL; ExFreePool(diffAreaFile); VspRelease(Extension->Root); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } MmBuildMdlForNonPagedPool(mdl); diffAreaFile->TableUpdateIrp->MdlAddress = mdl; InitializeListHead(&diffAreaFile->TableUpdateQueue); } VspRelease(Extension->Root); KeAcquireSpinLock(&filter->SpinLock, &irql); filter->AllocatedVolumeSpace += diffAreaFile->AllocatedFileSize; KeReleaseSpinLock(&filter->SpinLock, irql); diffAreaSpaceDeleted = 0; for (;;) { status = VspOpenDiffAreaFile(diffAreaFile, TRUE); if (status != STATUS_DISK_FULL) { break; } if (diffAreaSpaceDeleted >= 2*InitialDiffAreaAllocation) { status = VspOpenDiffAreaFile(diffAreaFile, FALSE); break; } status = VspTrimOlderSnapshotBeforeCreate(filter, &diffAreaSpaceDeleted); if (!NT_SUCCESS(status)) { status = VspOpenDiffAreaFile(diffAreaFile, FALSE); break; } } if (!NT_SUCCESS(status)) { goto Cleanup; } return status; Cleanup: if (Extension->DiffAreaFile) { diffAreaFile = Extension->DiffAreaFile; Extension->DiffAreaFile = NULL; if (diffAreaFile->FileHandle) { ZwClose(diffAreaFile->FileHandle); } KeAcquireSpinLock(&filter->SpinLock, &irql); filter->AllocatedVolumeSpace -= diffAreaFile->AllocatedFileSize; KeReleaseSpinLock(&filter->SpinLock, irql); if (diffAreaFile->TableUpdateIrp) { ExFreePool(MmGetMdlVirtualAddress( diffAreaFile->TableUpdateIrp->MdlAddress)); IoFreeMdl(diffAreaFile->TableUpdateIrp->MdlAddress); IoFreeIrp(diffAreaFile->TableUpdateIrp); } ASSERT(!diffAreaFile->FilterListEntryBeingUsed); ExFreePool(diffAreaFile); } return status; } VOID VspDeleteInitialDiffAreaFile( IN PVOLUME_EXTENSION Extension ) { PFILTER_EXTENSION filter = Extension->Filter; PLIST_ENTRY l, ll; PVSP_DIFF_AREA_FILE diffAreaFile; KIRQL irql; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; diffAreaFile = Extension->DiffAreaFile; Extension->DiffAreaFile = NULL; ASSERT(diffAreaFile); if (diffAreaFile->FileHandle) { ZwClose(diffAreaFile->FileHandle); } KeAcquireSpinLock(&filter->SpinLock, &irql); filter->AllocatedVolumeSpace -= diffAreaFile->AllocatedFileSize; filter->UsedVolumeSpace -= diffAreaFile->NextAvailable; KeReleaseSpinLock(&filter->SpinLock, irql); while (!IsListEmpty(&diffAreaFile->UnusedAllocationList)) { ll = RemoveHeadList(&diffAreaFile->UnusedAllocationList); diffAreaFileAllocation = CONTAINING_RECORD(ll, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } KeAcquireSpinLock(&diffAreaFile->Filter->SpinLock, &irql); if (diffAreaFile->FilterListEntryBeingUsed) { RemoveEntryList(&diffAreaFile->FilterListEntry); diffAreaFile->FilterListEntryBeingUsed = FALSE; } KeReleaseSpinLock(&diffAreaFile->Filter->SpinLock, irql); if (diffAreaFile->TableUpdateIrp) { ExFreePool(MmGetMdlVirtualAddress( diffAreaFile->TableUpdateIrp->MdlAddress)); IoFreeMdl(diffAreaFile->TableUpdateIrp->MdlAddress); IoFreeIrp(diffAreaFile->TableUpdateIrp); diffAreaFile->TableUpdateIrp = NULL; } ExFreePool(diffAreaFile); } VOID VspDiffAreaFillCompletion( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; ASSERT(context->Type == VSP_CONTEXT_TYPE_GROW_DIFF_AREA); KeSetEvent(&context->GrowDiffArea.Event, IO_NO_INCREMENT, FALSE); } NTSTATUS VspMarkFileAllocationInBitmap( IN PVOLUME_EXTENSION Extension, IN HANDLE FileHandle, IN PVSP_DIFF_AREA_FILE DiffAreaFile, IN PRTL_BITMAP BitmapToSet ) { NTSTATUS status; BOOLEAN isNtfs; IO_STATUS_BLOCK ioStatus; FILE_FS_SIZE_INFORMATION fsSize; ULONG bpc; STARTING_VCN_INPUT_BUFFER input; RETRIEVAL_POINTERS_BUFFER output; LONGLONG start, length, end, roundedStart, roundedEnd, s; ULONG startBit, numBits, i; KIRQL irql; PVOLUME_EXTENSION bitmapExtension; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; BOOLEAN isNegative; PVSP_CONTEXT context; status = VspIsNtfs(FileHandle, &isNtfs); if (!NT_SUCCESS(status) || !isNtfs) { return status; } status = ZwQueryVolumeInformationFile(FileHandle, &ioStatus, &fsSize, sizeof(fsSize), FileFsSizeInformation); if (!NT_SUCCESS(status)) { return status; } bpc = fsSize.BytesPerSector*fsSize.SectorsPerAllocationUnit; input.StartingVcn.QuadPart = 0; for (;;) { status = ZwFsControlFile(FileHandle, NULL, NULL, NULL, &ioStatus, FSCTL_GET_RETRIEVAL_POINTERS, &input, sizeof(input), &output, sizeof(output)); if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) { if (status == STATUS_END_OF_FILE) { status = STATUS_SUCCESS; } break; } if (!output.ExtentCount) { if (DiffAreaFile) { status = STATUS_INVALID_PARAMETER; } else { status = STATUS_SUCCESS; } break; } if (output.Extents[0].Lcn.QuadPart == -1) { ASSERT(!DiffAreaFile); if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; continue; } start = output.Extents[0].Lcn.QuadPart*bpc; length = output.Extents[0].NextVcn.QuadPart - output.StartingVcn.QuadPart; length *= bpc; end = start + length; roundedStart = start&(~(BLOCK_SIZE - 1)); roundedEnd = end&(~(BLOCK_SIZE - 1)); if (start != roundedStart) { roundedStart += BLOCK_SIZE; } if (DiffAreaFile) { if (IsListEmpty(&DiffAreaFile->Filter->VolumeList)) { bitmapExtension = NULL; } else { bitmapExtension = CONTAINING_RECORD( DiffAreaFile->Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); if (bitmapExtension->IsDead) { bitmapExtension = NULL; } } if (roundedStart > start) { diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = start; diffAreaFileAllocation->NLength = -(roundedStart - start); if (roundedStart > end) { diffAreaFileAllocation->NLength += roundedStart - end; } ASSERT(diffAreaFileAllocation->NLength); InsertTailList(&DiffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); } diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = roundedStart; diffAreaFileAllocation->NLength = 0; for (s = roundedStart; s < roundedEnd; s += BLOCK_SIZE) { isNegative = FALSE; if (bitmapExtension) { KeAcquireSpinLock(&bitmapExtension->SpinLock, &irql); if (bitmapExtension->VolumeBlockBitmap && !RtlCheckBit(bitmapExtension->VolumeBlockBitmap, s>>BLOCK_SHIFT)) { isNegative = TRUE; } KeReleaseSpinLock(&bitmapExtension->SpinLock, irql); } if (isNegative) { if (diffAreaFileAllocation->NLength <= 0) { diffAreaFileAllocation->NLength -= BLOCK_SIZE; } else { InsertTailList(&DiffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { return STATUS_INSUFFICIENT_RESOURCES; } diffAreaFileAllocation->Offset = s; diffAreaFileAllocation->NLength = -BLOCK_SIZE; } } else { if (diffAreaFileAllocation->NLength >= 0) { diffAreaFileAllocation->NLength += BLOCK_SIZE; } else { InsertTailList(&DiffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { return STATUS_INSUFFICIENT_RESOURCES; } diffAreaFileAllocation->Offset = s; diffAreaFileAllocation->NLength = BLOCK_SIZE; } } } if (diffAreaFileAllocation->NLength) { InsertTailList(&DiffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); } else { ExFreePool(diffAreaFileAllocation); } if (end > roundedEnd && roundedEnd >= roundedStart) { diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { status = STATUS_INSUFFICIENT_RESOURCES; break; } diffAreaFileAllocation->Offset = roundedEnd; diffAreaFileAllocation->NLength = -(end - roundedEnd); InsertTailList(&DiffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); } if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; continue; } if (roundedStart >= roundedEnd) { if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; continue; } startBit = (ULONG) (roundedStart>>BLOCK_SHIFT); numBits = (ULONG) ((roundedEnd - roundedStart)>>BLOCK_SHIFT); if (BitmapToSet) { RtlSetBits(BitmapToSet, startBit, numBits); } else { KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->VolumeBlockBitmap) { if (Extension->IgnorableProduct) { for (i = 0; i < numBits; i++) { if (RtlCheckBit(Extension->IgnorableProduct, i + startBit)) { RtlSetBit(Extension->VolumeBlockBitmap, i + startBit); } } } else { RtlSetBits(Extension->VolumeBlockBitmap, startBit, numBits); } } KeReleaseSpinLock(&Extension->SpinLock, irql); } if (status != STATUS_BUFFER_OVERFLOW) { break; } input.StartingVcn.QuadPart = output.Extents[0].NextVcn.QuadPart; } if (DiffAreaFile && Extension->IsPersistent && !Extension->NoDiffAreaFill) { context = VspAllocateContext(Extension->Root); if (!context) { return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_GROW_DIFF_AREA; ExInitializeWorkItem(&context->WorkItem, VspDiffAreaFillCompletion, context); context->GrowDiffArea.Extension = Extension; context->GrowDiffArea.ExtentList = DiffAreaFile->UnusedAllocationList; ASSERT(!IsListEmpty(&DiffAreaFile->UnusedAllocationList)); context->GrowDiffArea.ExtentList.Flink->Blink = &context->GrowDiffArea.ExtentList; context->GrowDiffArea.ExtentList.Blink->Flink = &context->GrowDiffArea.ExtentList; KeInitializeSpinLock(&context->GrowDiffArea.SpinLock); context->GrowDiffArea.CurrentEntry = context->GrowDiffArea.ExtentList.Flink; context->GrowDiffArea.CurrentEntryOffset = 0; context->GrowDiffArea.TargetObject = DiffAreaFile->Filter->TargetObject; ObReferenceObject(context->GrowDiffArea.TargetObject); KeInitializeEvent(&context->GrowDiffArea.Event, NotificationEvent, FALSE); VspLaunchDiffAreaFill(context); KeWaitForSingleObject(&context->GrowDiffArea.Event, Executive, KernelMode, FALSE, NULL); DiffAreaFile->UnusedAllocationList.Blink->Flink = &DiffAreaFile->UnusedAllocationList; DiffAreaFile->UnusedAllocationList.Flink->Blink = &DiffAreaFile->UnusedAllocationList; VspReleaseNonPagedResource(Extension); status = context->GrowDiffArea.ResultStatus; VspFreeContext(Extension->Root, context); } return status; } NTSTATUS VspSetDiffAreaBlocksInBitmap( IN PVOLUME_EXTENSION Extension ) { PVSP_DIFF_AREA_FILE diffAreaFile; PFILTER_EXTENSION diffAreaFilter; KIRQL irql; NTSTATUS status, status2; PLIST_ENTRY l; diffAreaFile = Extension->DiffAreaFile; ASSERT(diffAreaFile); diffAreaFilter = diffAreaFile->Filter; KeAcquireSpinLock(&diffAreaFilter->SpinLock, &irql); ASSERT(!diffAreaFile->FilterListEntryBeingUsed); InsertTailList(&diffAreaFilter->DiffAreaFilesOnThisFilter, &diffAreaFile->FilterListEntry); diffAreaFile->FilterListEntryBeingUsed = TRUE; KeReleaseSpinLock(&diffAreaFilter->SpinLock, irql); status = VspMarkFileAllocationInBitmap(Extension, diffAreaFile->FileHandle, diffAreaFile, NULL); if (!NT_SUCCESS(status)) { VspLogError(Extension, diffAreaFilter, VS_CANT_MAP_DIFF_AREA_FILE, status, 0, FALSE); KeAcquireSpinLock(&diffAreaFilter->SpinLock, &irql); RemoveEntryList(&diffAreaFile->FilterListEntry); diffAreaFile->FilterListEntryBeingUsed = FALSE; KeReleaseSpinLock(&diffAreaFilter->SpinLock, irql); return status; } return STATUS_SUCCESS; } NTSTATUS VspCreateInitialHeap( IN PVOLUME_EXTENSION Extension, IN BOOLEAN CallerHoldingSemaphore ) { PVSP_CONTEXT context; NTSTATUS status; context = VspAllocateContext(Extension->Root); if (!context) { return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = Extension; if (CallerHoldingSemaphore) { context->Extension.Irp = (PIRP) 1; } else { context->Extension.Irp = NULL; } ObReferenceObject(Extension->DeviceObject); VspCreateHeap(context); if (!Extension->NextDiffAreaFileMap) { return STATUS_INSUFFICIENT_RESOURCES; } ASSERT(!Extension->DiffAreaFileMap); Extension->DiffAreaFileMap = Extension->NextDiffAreaFileMap; Extension->DiffAreaFileMapSize = Extension->NextDiffAreaFileMapSize; Extension->NextAvailable = 0; Extension->NextDiffAreaFileMap = NULL; context = VspAllocateContext(Extension->Root); if (!context) { status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, Extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); Extension->DiffAreaFileMap = NULL; return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = Extension; if (CallerHoldingSemaphore) { context->Extension.Irp = (PIRP) 1; } else { context->Extension.Irp = NULL; } ObReferenceObject(Extension->DeviceObject); VspCreateHeap(context); if (!Extension->NextDiffAreaFileMap) { status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, Extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); Extension->DiffAreaFileMap = NULL; return STATUS_INSUFFICIENT_RESOURCES; } return STATUS_SUCCESS; } NTSTATUS VspReadInitialBitmap( IN PVOLUME_EXTENSION Extension, IN OUT PRTL_BITMAP Bitmap ) { PVOID buffer; PMDL mdl; PIRP irp; LONGLONG offset; PDEVICE_OBJECT targetObject; NTSTATUS status; PVSP_BLOCK_INITIAL_BITMAP initialBitmap; LONGLONG bytesCopied, bytesToCopy, totalBytesToCopy; buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { return STATUS_INSUFFICIENT_RESOURCES; } mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buffer); return STATUS_INSUFFICIENT_RESOURCES; } irp = IoAllocateIrp((CCHAR) Extension->Root->StackSize, FALSE); if (!irp) { IoFreeMdl(mdl); ExFreePool(buffer); return STATUS_INSUFFICIENT_RESOURCES; } irp->MdlAddress = mdl; MmBuildMdlForNonPagedPool(mdl); offset = Extension->DiffAreaFile->InitialBitmapVolumeOffset; targetObject = Extension->DiffAreaFile->Filter->TargetObject; status = STATUS_UNSUCCESSFUL; initialBitmap = (PVSP_BLOCK_INITIAL_BITMAP) buffer; bytesCopied = 0; totalBytesToCopy = (Bitmap->SizeOfBitMap + 7)/8; while (offset) { status = VspSynchronousIo(irp, targetObject, IRP_MJ_READ, offset, 0); if (!NT_SUCCESS(status)) { break; } if (!IsEqualGUID(initialBitmap->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || initialBitmap->Header.Version != VOLSNAP_PERSISTENT_VERSION || initialBitmap->Header.BlockType != VSP_BLOCK_TYPE_INITIAL_BITMAP || initialBitmap->Header.ThisVolumeOffset != offset || initialBitmap->Header.NextVolumeOffset == offset) { status = STATUS_INVALID_PARAMETER; break; } bytesToCopy = (BLOCK_SIZE - VSP_OFFSET_TO_START_OF_BITMAP); if (bytesToCopy + bytesCopied > totalBytesToCopy) { bytesToCopy = totalBytesToCopy - bytesCopied; } RtlCopyMemory((PCHAR) Bitmap->Buffer + bytesCopied, (PCHAR) initialBitmap + VSP_OFFSET_TO_START_OF_BITMAP, (ULONG) bytesToCopy); bytesCopied += bytesToCopy; offset = initialBitmap->Header.NextVolumeOffset; } ExFreePool(buffer); IoFreeMdl(mdl); IoFreeIrp(irp); return status; } VOID VspWriteInitialBitmap( IN PVOLUME_EXTENSION Extension, IN OUT PRTL_BITMAP Bitmap ) { ULONG numBytesPerBlock, bitmapBytes, numBlocks, i; PLONGLONG volumeOffset, fileOffset; PVOID buffer; PMDL mdl; PIRP irp; NTSTATUS status; PVSP_BLOCK_INITIAL_BITMAP initialBitmap; ULONG bytesCopied, bytesToCopy; PDEVICE_OBJECT targetObject; CHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_DIFF_AREA diffAreaControlItem; numBytesPerBlock = BLOCK_SIZE - VSP_OFFSET_TO_START_OF_BITMAP; bitmapBytes = (Bitmap->SizeOfBitMap + 7)/8; numBlocks = (bitmapBytes + numBytesPerBlock - 1)/numBytesPerBlock; ASSERT(numBlocks); volumeOffset = (PLONGLONG) ExAllocatePoolWithTag(NonPagedPool, numBlocks*sizeof(LONGLONG), VOLSNAP_TAG_SHORT_TERM); if (!volumeOffset) { return; } fileOffset = (PLONGLONG) ExAllocatePoolWithTag(NonPagedPool, numBlocks*sizeof(LONGLONG), VOLSNAP_TAG_SHORT_TERM); if (!fileOffset) { ExFreePool(volumeOffset); return; } buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { ExFreePool(fileOffset); ExFreePool(volumeOffset); return; } mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buffer); ExFreePool(fileOffset); ExFreePool(volumeOffset); return; } irp = IoAllocateIrp((CCHAR) Extension->Root->StackSize, FALSE); if (!irp) { IoFreeMdl(mdl); ExFreePool(buffer); ExFreePool(fileOffset); ExFreePool(volumeOffset); return; } irp->MdlAddress = mdl; MmBuildMdlForNonPagedPool(mdl); VspAcquireNonPagedResource(Extension, NULL, FALSE); for (i = 0; i < numBlocks; i++) { status = VspAllocateDiffAreaSpace(Extension, &volumeOffset[i], &fileOffset[i], NULL, NULL); if (!NT_SUCCESS(status)) { break; } } if (i < numBlocks) { goto Cleanup; } initialBitmap = (PVSP_BLOCK_INITIAL_BITMAP) buffer; bytesCopied = 0; targetObject = Extension->DiffAreaFile->Filter->TargetObject; for (i = 0; i < numBlocks; i++) { RtlZeroMemory(initialBitmap, BLOCK_SIZE); initialBitmap->Header.Signature = VSP_DIFF_AREA_FILE_GUID; initialBitmap->Header.Version = VOLSNAP_PERSISTENT_VERSION; initialBitmap->Header.BlockType = VSP_BLOCK_TYPE_INITIAL_BITMAP; initialBitmap->Header.ThisFileOffset = fileOffset[i]; initialBitmap->Header.ThisVolumeOffset = volumeOffset[i]; if (i + 1 < numBlocks) { initialBitmap->Header.NextVolumeOffset = volumeOffset[i + 1]; } else { initialBitmap->Header.NextVolumeOffset = 0; } bytesToCopy = (BLOCK_SIZE - VSP_OFFSET_TO_START_OF_BITMAP); if (bytesToCopy + bytesCopied > bitmapBytes) { bytesToCopy = bitmapBytes - bytesCopied; } RtlCopyMemory((PCHAR) initialBitmap + VSP_OFFSET_TO_START_OF_BITMAP, (PCHAR) Bitmap->Buffer + bytesCopied, (ULONG) bytesToCopy); bytesCopied += bytesToCopy; status = VspSynchronousIo(irp, targetObject, IRP_MJ_WRITE, volumeOffset[i], 0); if (!NT_SUCCESS(status)) { goto Cleanup; } } status = VspIoControlItem(Extension->DiffAreaFile->Filter, VSP_CONTROL_ITEM_TYPE_DIFF_AREA, &Extension->SnapshotGuid, FALSE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { goto Cleanup; } diffAreaControlItem = (PVSP_CONTROL_ITEM_DIFF_AREA) controlItemBuffer; diffAreaControlItem->InitialBitmapVolumeOffset = volumeOffset[0]; status = VspIoControlItem(Extension->DiffAreaFile->Filter, VSP_CONTROL_ITEM_TYPE_DIFF_AREA, &Extension->SnapshotGuid, TRUE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { goto Cleanup; } Extension->DiffAreaFile->InitialBitmapVolumeOffset = volumeOffset[0]; Cleanup: VspReleaseNonPagedResource(Extension); IoFreeIrp(irp); IoFreeMdl(mdl); ExFreePool(buffer); ExFreePool(fileOffset); ExFreePool(volumeOffset); } VOID VspSetOfflineBit( IN PVOLUME_EXTENSION Extension, IN BOOLEAN BitState ) { NTSTATUS status; CHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; VspAcquireNonPagedResource(Extension, NULL, FALSE); if (Extension->IsOffline) { if (BitState) { VspReleaseNonPagedResource(Extension); return; } InterlockedExchange(&Extension->IsOffline, FALSE); } else { if (!BitState) { VspReleaseNonPagedResource(Extension); return; } InterlockedExchange(&Extension->IsOffline, TRUE); } if (!Extension->IsPersistent) { VspReleaseNonPagedResource(Extension); return; } status = VspIoControlItem(Extension->Filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, FALSE, controlItemBuffer, FALSE); if (!NT_SUCCESS(status)) { ASSERT(FALSE); VspReleaseNonPagedResource(Extension); return; } if (BitState) { snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_OFFLINE; } else { snapshotControlItem->SnapshotControlItemFlags &= ~VSP_SNAPSHOT_CONTROL_ITEM_FLAG_OFFLINE; } status = VspIoControlItem(Extension->Filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, TRUE, controlItemBuffer, FALSE); ASSERT(NT_SUCCESS(status)); VspReleaseNonPagedResource(Extension); } NTSTATUS VspComputeIgnorableBitmap( IN PVOLUME_EXTENSION Extension, IN OUT PRTL_BITMAP Bitmap ) { PFILTER_EXTENSION filter = Extension->Filter; KIRQL irql; ULONG bitmapSize; PVOID bitmapBuffer; RTL_BITMAP bitmap; WCHAR nameBuffer[150]; UNICODE_STRING name, guidString; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE h, fileHandle; IO_STATUS_BLOCK ioStatus; CHAR buffer[200]; PFILE_NAMES_INFORMATION fileNamesInfo; BOOLEAN restartScan, moveEntries; PLIST_ENTRY l; PVOLUME_EXTENSION e; PTRANSLATION_TABLE_ENTRY p; VspAcquire(Extension->Root); if (Extension->IsDead) { VspRelease(Extension->Root); return STATUS_UNSUCCESSFUL; } if (Bitmap) { bitmapBuffer = NULL; } else { KeAcquireSpinLock(&Extension->SpinLock, &irql); if (!Extension->VolumeBlockBitmap) { KeReleaseSpinLock(&Extension->SpinLock, irql); VspRelease(Extension->Root); return STATUS_INVALID_PARAMETER; } bitmapSize = Extension->VolumeBlockBitmap->SizeOfBitMap; KeReleaseSpinLock(&Extension->SpinLock, irql); bitmapBuffer = ExAllocatePoolWithTag( NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { VspRelease(Extension->Root); return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(&bitmap, (PULONG) bitmapBuffer, bitmapSize); RtlClearAllBits(&bitmap); Bitmap = &bitmap; } if (Extension->DiffAreaFile->InitialBitmapVolumeOffset) { status = VspReadInitialBitmap(Extension, Bitmap); if (NT_SUCCESS(status)) { goto AddInTable; } RtlClearAllBits(Bitmap); } VspRelease(Extension->Root); if (Extension->IsOffline) { VspSetOfflineBit(Extension, FALSE); } swprintf(nameBuffer, L"\\Device\\HarddiskVolumeShadowCopy%d\\pagefile.sys", Extension->VolumeNumber); RtlInitUnicodeString(&name, nameBuffer); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(status)) { if (!VspHasStrongAcl(h, &status)) { ZwClose(h); } } if (NT_SUCCESS(status)) { VspMarkFileAllocationInBitmap(NULL, h, NULL, Bitmap); ZwClose(h); } swprintf(nameBuffer, L"\\Device\\HarddiskVolumeShadowCopy%d\\hiberfil.sys", Extension->VolumeNumber); RtlInitUnicodeString(&name, nameBuffer); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(status)) { if (!VspHasStrongAcl(h, &status)) { ZwClose(h); } } if (NT_SUCCESS(status)) { VspMarkFileAllocationInBitmap(NULL, h, NULL, Bitmap); ZwClose(h); } swprintf(nameBuffer, L"\\Device\\HarddiskVolumeShadowCopy%d\\System Volume Information\\", Extension->VolumeNumber); RtlInitUnicodeString(&name, nameBuffer); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { if (status == STATUS_OBJECT_NAME_NOT_FOUND) { status = STATUS_SUCCESS; } if (bitmapBuffer) { ExFreePool(bitmapBuffer); } return status; } status = RtlStringFromGUID(VSP_DIFF_AREA_FILE_GUID, &guidString); if (!NT_SUCCESS(status)) { ZwClose(h); if (bitmapBuffer) { ExFreePool(bitmapBuffer); } return status; } name.Buffer = nameBuffer; name.Length = sizeof(WCHAR) + guidString.Length; name.MaximumLength = name.Length + sizeof(WCHAR); name.Buffer[0] = '*'; RtlCopyMemory(&name.Buffer[1], guidString.Buffer, guidString.Length); name.Buffer[name.Length/sizeof(WCHAR)] = 0; ExFreePool(guidString.Buffer); fileNamesInfo = (PFILE_NAMES_INFORMATION) buffer; restartScan = TRUE; for (;;) { status = ZwQueryDirectoryFile(h, NULL, NULL, NULL, &ioStatus, fileNamesInfo, 200, FileNamesInformation, TRUE, restartScan ? &name : NULL, restartScan); restartScan = FALSE; if (!NT_SUCCESS(status)) { break; } name.Length = name.MaximumLength = (USHORT) fileNamesInfo->FileNameLength; name.Buffer = fileNamesInfo->FileName; InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, h, NULL); status = ZwOpenFile(&fileHandle, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { continue; } VspMarkFileAllocationInBitmap(NULL, fileHandle, NULL, Bitmap); ZwClose(fileHandle); } ZwClose(h); status = VspMarkFreeSpaceInBitmap(Extension, NULL, Bitmap); if (!NT_SUCCESS(status)) { if (bitmapBuffer) { ExFreePool(bitmapBuffer); } return status; } VspAcquire(Extension->Root); if (Extension->IsDead) { VspRelease(Extension->Root); if (bitmapBuffer) { ExFreePool(bitmapBuffer); } return STATUS_UNSUCCESSFUL; } if (Extension->IsPersistent) { VspWriteInitialBitmap(Extension, Bitmap); } AddInTable: for (l = &Extension->ListEntry; l != &filter->VolumeList; l = l->Flink) { e = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); VspAcquirePagedResource(e, NULL); moveEntries = FALSE; _try { p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&e->VolumeBlockTable, TRUE); while (p) { RtlSetBit(Bitmap, (ULONG) (p->VolumeOffset>>BLOCK_SHIFT)); if (p->Flags&VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY) { moveEntries = TRUE; } p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&e->VolumeBlockTable, FALSE); } if (moveEntries) { p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&e->VolumeBlockTable, TRUE); while (p) { if (p->Flags&VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY) { RtlClearBit(Bitmap, (ULONG) (p->TargetOffset>>BLOCK_SHIFT)); } p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&e->VolumeBlockTable, FALSE); } } } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } VspReleasePagedResource(e); if (!NT_SUCCESS(status)) { break; } } VspRelease(Extension->Root); if (bitmapBuffer) { ExFreePool(bitmapBuffer); } return status; } VOID VspAndBitmaps( IN OUT PRTL_BITMAP BaseBitmap, IN PRTL_BITMAP FactorBitmap ) { ULONG n, i; PULONG p, q; n = (BaseBitmap->SizeOfBitMap + 8*sizeof(ULONG) - 1)/(8*sizeof(ULONG)); p = BaseBitmap->Buffer; q = FactorBitmap->Buffer; for (i = 0; i < n; i++) { *p++ &= *q++; } } NTSTATUS VspComputeIgnorableProduct( IN PVOLUME_EXTENSION Extension ) { PFILTER_EXTENSION filter = Extension->Filter; ULONG bitmapSize; PVOID bitmapBuffer; RTL_BITMAP bitmap; ULONG i, j; PLIST_ENTRY l; PVOLUME_EXTENSION e; NTSTATUS status; KIRQL irql; KeAcquireSpinLock(&Extension->SpinLock, &irql); if (!Extension->IgnorableProduct) { KeReleaseSpinLock(&Extension->SpinLock, irql); return STATUS_INVALID_PARAMETER; } bitmapSize = Extension->IgnorableProduct->SizeOfBitMap; RtlSetAllBits(Extension->IgnorableProduct); KeReleaseSpinLock(&Extension->SpinLock, irql); bitmapBuffer = ExAllocatePoolWithTag( NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(&bitmap, (PULONG) bitmapBuffer, bitmapSize); for (i = 1; ; i++) { RtlClearAllBits(&bitmap); VspAcquire(Extension->Root); l = filter->VolumeList.Blink; if (l != &Extension->ListEntry) { VspRelease(Extension->Root); ExFreePool(bitmapBuffer); return STATUS_INVALID_PARAMETER; } j = 0; for (;;) { if (l == &filter->VolumeList) { break; } j++; if (j == i) { break; } l = l->Blink; } if (j < i) { VspRelease(Extension->Root); break; } e = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); ObReferenceObject(e->DeviceObject); VspRelease(Extension->Root); status = VspComputeIgnorableBitmap(e, &bitmap); if (!NT_SUCCESS(status)) { VspAcquire(Extension->Root); if (e->IsDead) { VspRelease(Extension->Root); ObDereferenceObject(e->DeviceObject); ExFreePool(bitmapBuffer); return STATUS_SUCCESS; } VspRelease(Extension->Root); ObDereferenceObject(e->DeviceObject); ExFreePool(bitmapBuffer); return status; } ObDereferenceObject(e->DeviceObject); KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->IgnorableProduct) { VspAndBitmaps(Extension->IgnorableProduct, &bitmap); } KeReleaseSpinLock(&Extension->SpinLock, irql); } ExFreePool(bitmapBuffer); return STATUS_SUCCESS; } VOID VspQueryMinimumDiffAreaFileSize( IN PDO_EXTENSION RootExtension, OUT PLONGLONG MinDiffAreaFileSize ) { ULONG zero, size; RTL_QUERY_REGISTRY_TABLE queryTable[2]; NTSTATUS status; zero = 0; RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"MinDiffAreaFileSize"; queryTable[0].EntryContext = &size; queryTable[0].DefaultType = REG_DWORD; queryTable[0].DefaultData = &zero; queryTable[0].DefaultLength = sizeof(ULONG); status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RootExtension->RegistryPath.Buffer, queryTable, NULL, NULL); if (!NT_SUCCESS(status)) { size = zero; } *MinDiffAreaFileSize = ((LONGLONG) size)*1024*1024; } NTSTATUS VspCreateStartBlock( IN PFILTER_EXTENSION Filter, IN LONGLONG ControlBlockOffset, IN LONGLONG MaximumDiffAreaSpace ) /*++ Routine Description: This routine creates the start block in the NTFS boot code. Arguments: Filter - Supplies the filter extension. Return Value: NTSTATUS --*/ { PVOID buffer; KEVENT event; LARGE_INTEGER byteOffset; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; PVSP_BLOCK_START startBlock; buffer = ExAllocatePoolWithTag(NonPagedPool, BYTES_IN_BOOT_AREA > PAGE_SIZE ? BYTES_IN_BOOT_AREA : PAGE_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { return STATUS_INSUFFICIENT_RESOURCES; } KeInitializeEvent(&event, NotificationEvent, FALSE); byteOffset.QuadPart = 0; irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, Filter->TargetObject, buffer, BYTES_IN_BOOT_AREA, &byteOffset, &event, &ioStatus); if (!irp) { ExFreePool(buffer); return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(Filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { ExFreePool(buffer); return status; } if (!VspIsNtfsBootSector(Filter, buffer)) { ExFreePool(buffer); return STATUS_SUCCESS; } startBlock = (PVSP_BLOCK_START) ((PCHAR) buffer + VSP_START_BLOCK_OFFSET); if (ControlBlockOffset || MaximumDiffAreaSpace) { startBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; startBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; startBlock->Header.BlockType = VSP_BLOCK_TYPE_START; startBlock->Header.ThisFileOffset = VSP_START_BLOCK_OFFSET; startBlock->Header.ThisVolumeOffset = VSP_START_BLOCK_OFFSET; startBlock->Header.NextVolumeOffset = 0; startBlock->FirstControlBlockVolumeOffset = ControlBlockOffset; startBlock->MaximumDiffAreaSpace = MaximumDiffAreaSpace; } else { RtlZeroMemory(startBlock, sizeof(VSP_BLOCK_START)); } KeInitializeEvent(&event, NotificationEvent, FALSE); byteOffset.QuadPart = 0; irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Filter->TargetObject, buffer, BYTES_IN_BOOT_AREA, &byteOffset, &event, &ioStatus); if (!irp) { ExFreePool(buffer); return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(Filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } ExFreePool(buffer); return status; } VOID VspCreateSnapshotOnDiskIrp( IN PFILTER_EXTENSION Filter ) { PVOID buffer; PMDL mdl; ASSERT(!Filter->SnapshotOnDiskIrp); buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { return; } mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buffer); return; } MmBuildMdlForNonPagedPool(mdl); Filter->SnapshotOnDiskIrp = IoAllocateIrp( (CCHAR) Filter->Root->StackSize, FALSE); if (!Filter->SnapshotOnDiskIrp) { IoFreeMdl(mdl); ExFreePool(buffer); return; } Filter->SnapshotOnDiskIrp->MdlAddress = mdl; } NTSTATUS VspCreateInitialControlBlockFile( IN PFILTER_EXTENSION Filter ) /*++ Routine Description: This routine creates the initial control block file on the given filter. Arguments: Filter - Supplies the filter extension. Return Value: NTSTATUS --*/ { NTSTATUS status; UNICODE_STRING fileName; PWCHAR star; OBJECT_ATTRIBUTES oa; LARGE_INTEGER fileSize; HANDLE h; IO_STATUS_BLOCK ioStatus; LIST_ENTRY extentList; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PVSP_BLOCK_CONTROL controlBlock; PVOLUME_EXTENSION bitmapExtension; LONGLONG lastVolumeOffset; BOOLEAN isNtfs; PSECURITY_DESCRIPTOR securityDescriptor; PACL acl; status = VspCreateDiffAreaFileName(Filter->TargetObject, NULL, &fileName, TRUE, NULL); if (!NT_SUCCESS(status)) { return status; } star = fileName.Buffer + fileName.Length/sizeof(WCHAR) - 39; RtlMoveMemory(star, star + 1, 38*sizeof(WCHAR)); fileName.Length -= sizeof(WCHAR); fileName.Buffer[fileName.Length/sizeof(WCHAR)] = 0; status = VspCreateSecurityDescriptor(&securityDescriptor, &acl); if (!NT_SUCCESS(status)) { ExFreePool(fileName.Buffer); return status; } InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, securityDescriptor); fileSize.QuadPart = LARGEST_NTFS_CLUSTER; ASSERT(LARGEST_NTFS_CLUSTER >= BLOCK_SIZE); KeWaitForSingleObject(&Filter->ControlBlockFileHandleReady, Executive, KernelMode, FALSE, NULL); status = ZwCreateFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &oa, &ioStatus, &fileSize, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, 0, FILE_OVERWRITE_IF, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION, NULL, 0); ExFreePool(fileName.Buffer); if (!NT_SUCCESS(status)) { return status; } status = VspSetFileSize(h, fileSize.QuadPart); if (!NT_SUCCESS(status)) { ZwClose(h); return status; } status = VspIsNtfs(h, &isNtfs); if (!NT_SUCCESS(status) || !isNtfs) { ZwClose(h); return STATUS_INVALID_PARAMETER; } VspAcquire(Filter->Root); if (!IsListEmpty(&Filter->VolumeList)) { bitmapExtension = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); ObReferenceObject(bitmapExtension->DeviceObject); } else { bitmapExtension = NULL; } VspRelease(Filter->Root); status = VspOptimizeDiffAreaFileLocation(Filter, h, bitmapExtension, 0, LARGEST_NTFS_CLUSTER); if (!NT_SUCCESS(status)) { if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } ZwClose(h); return status; } status = VspPinFile(Filter->TargetObject, h); if (!NT_SUCCESS(status)) { if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } ZwClose(h); return status; } status = VspQueryListOfExtents(h, 0, &extentList, bitmapExtension, FALSE); if (bitmapExtension) { ObDereferenceObject(bitmapExtension->DeviceObject); } if (!NT_SUCCESS(status)) { ZwClose(h); return status; } for (l = extentList.Flink; l != &extentList; l = l->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength < 0) { ZwClose(h); return STATUS_INSUFFICIENT_RESOURCES; } } VspAcquireNonPagedResource(Filter, NULL, FALSE); if (!Filter->SnapshotOnDiskIrp) { VspCreateSnapshotOnDiskIrp(Filter); if (!Filter->SnapshotOnDiskIrp) { VspReleaseNonPagedResource(Filter); while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } ZwClose(h); return STATUS_INSUFFICIENT_RESOURCES; } } controlBlock = (PVSP_BLOCK_CONTROL) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); RtlZeroMemory(controlBlock, BLOCK_SIZE); controlBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; controlBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; controlBlock->Header.BlockType = VSP_BLOCK_TYPE_CONTROL; controlBlock->Header.ThisFileOffset = LARGEST_NTFS_CLUSTER; lastVolumeOffset = 0; l = extentList.Blink; while (l != &extentList) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (!diffAreaFileAllocation->NLength) { l = l->Blink; continue; } diffAreaFileAllocation->NLength -= BLOCK_SIZE; controlBlock->Header.ThisFileOffset -= BLOCK_SIZE; controlBlock->Header.ThisVolumeOffset = diffAreaFileAllocation->Offset + diffAreaFileAllocation->NLength; controlBlock->Header.NextVolumeOffset = lastVolumeOffset; lastVolumeOffset = controlBlock->Header.ThisVolumeOffset; status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, controlBlock->Header.ThisVolumeOffset, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } ZwClose(h); return status; } } VspReleaseNonPagedResource(Filter); diffAreaFileAllocation = CONTAINING_RECORD(extentList.Flink, DIFF_AREA_FILE_ALLOCATION, ListEntry); VspAcquireNonPagedResource(Filter, NULL, FALSE); status = VspCreateStartBlock(Filter, diffAreaFileAllocation->Offset, Filter->MaximumVolumeSpace); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } ZwClose(h); return status; } ASSERT(!Filter->ControlBlockFileHandle); Filter->FirstControlBlockVolumeOffset = diffAreaFileAllocation->Offset; VspReleaseNonPagedResource(Filter); InterlockedExchangePointer(&Filter->ControlBlockFileHandle, h); VspAcquire(Filter->Root); if (Filter->IsRemoved) { VspRelease(Filter->Root); h = InterlockedExchangePointer(&Filter->ControlBlockFileHandle, NULL); if (h) { ZwClose(h); } } VspRelease(Filter->Root); while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } return STATUS_SUCCESS; } NTSTATUS VspAddControlBlocks( IN PFILTER_EXTENSION Filter, IN LONGLONG OffsetOfLastControlBlock ) /*++ Routine Description: This routine adds new control blocks to the control block list. Arguments: Filter - Supplies the filter extension. OffsetOfLastControlBlock - Supplies the offset of the last control block. Return Value: NTSTATUS --*/ { HANDLE handle; NTSTATUS status; LONGLONG fileSize; PVOLUME_EXTENSION extension; LIST_ENTRY extentList; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PVSP_BLOCK_CONTROL controlBlock; handle = Filter->ControlBlockFileHandle; if (!handle) { return STATUS_INVALID_PARAMETER; } status = VspQueryFileSize(handle, &fileSize); if (!NT_SUCCESS(status)) { return status; } ASSERT(fileSize); for (;;) { status = VspSetFileSize(handle, fileSize + LARGEST_NTFS_CLUSTER); if (!NT_SUCCESS(status)) { return status; } VspAcquire(Filter->Root); if (IsListEmpty(&Filter->VolumeList)) { extension = NULL; } else { extension = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); if (extension->IsDead) { extension = NULL; } else { ObReferenceObject(extension->DeviceObject); } } VspRelease(Filter->Root); VspOptimizeDiffAreaFileLocation(Filter, handle, extension, fileSize, fileSize + LARGEST_NTFS_CLUSTER); status = VspQueryListOfExtents(handle, fileSize, &extentList, extension, FALSE); if (extension) { ObDereferenceObject(extension->DeviceObject); } if (!NT_SUCCESS(status)) { return status; } for (l = extentList.Flink; l != &extentList; l = l->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength > 0) { break; } } if (l != &extentList) { break; } while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } fileSize += LARGEST_NTFS_CLUSTER; } VspAcquireNonPagedResource(Filter, NULL, FALSE); if (!Filter->SnapshotOnDiskIrp) { VspReleaseNonPagedResource(Filter); return STATUS_INVALID_PARAMETER; } controlBlock = (PVSP_BLOCK_CONTROL) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); l = extentList.Flink; while (l != &extentList) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength <= 0) { l = l->Flink; fileSize -= diffAreaFileAllocation->NLength; continue; } RtlZeroMemory(controlBlock, BLOCK_SIZE); controlBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; controlBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; controlBlock->Header.BlockType = VSP_BLOCK_TYPE_CONTROL; controlBlock->Header.ThisFileOffset = fileSize; controlBlock->Header.ThisVolumeOffset = diffAreaFileAllocation->Offset; controlBlock->Header.NextVolumeOffset = 0; status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, controlBlock->Header.ThisVolumeOffset, 0); if (!NT_SUCCESS(status)) { break; } status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, OffsetOfLastControlBlock, 0); if (!NT_SUCCESS(status)) { break; } controlBlock->Header.NextVolumeOffset = diffAreaFileAllocation->Offset; status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, controlBlock->Header.ThisVolumeOffset, 0); if (!NT_SUCCESS(status)) { break; } OffsetOfLastControlBlock = diffAreaFileAllocation->Offset; diffAreaFileAllocation->NLength -= BLOCK_SIZE; diffAreaFileAllocation->Offset += BLOCK_SIZE; fileSize += BLOCK_SIZE; } VspReleaseNonPagedResource(Filter); while (!IsListEmpty(&extentList)) { l = RemoveHeadList(&extentList); diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); ExFreePool(diffAreaFileAllocation); } return status; } NTSTATUS VspAddControlItemInfoToLookupTable( IN PFILTER_EXTENSION Filter, IN PVSP_CONTROL_ITEM_SNAPSHOT ControlItem, IN PBOOLEAN IsComplete ) { PVSP_CONTROL_ITEM_DIFF_AREA diffAreaControlItem; VSP_LOOKUP_TABLE_ENTRY lookupEntryBuffer; PVSP_LOOKUP_TABLE_ENTRY lookupEntry, le; PLIST_ENTRY l; if (IsComplete) { *IsComplete = FALSE; } if (ControlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT) { if (ControlItem->Reserved || ControlItem->VolumeSnapshotSize <= 0 || ControlItem->SnapshotOrderNumber < 0 || (ControlItem->SnapshotControlItemFlags& (~VSP_SNAPSHOT_CONTROL_ITEM_FLAG_ALL))) { return STATUS_INVALID_PARAMETER; } } else if (ControlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_DIFF_AREA) { diffAreaControlItem = (PVSP_CONTROL_ITEM_DIFF_AREA) ControlItem; if (diffAreaControlItem->Reserved || diffAreaControlItem->DiffAreaStartingVolumeOffset <= 0 || (diffAreaControlItem->DiffAreaStartingVolumeOffset& (BLOCK_SIZE - 1)) || diffAreaControlItem->ApplicationInfoStartingVolumeOffset <= 0 || (diffAreaControlItem->ApplicationInfoStartingVolumeOffset& (BLOCK_SIZE - 1)) || diffAreaControlItem->DiffAreaLocationDescriptionVolumeOffset <= 0 || (diffAreaControlItem->DiffAreaLocationDescriptionVolumeOffset& (BLOCK_SIZE - 1)) || diffAreaControlItem->InitialBitmapVolumeOffset < 0 || (diffAreaControlItem->InitialBitmapVolumeOffset&(BLOCK_SIZE - 1))) { return STATUS_INVALID_PARAMETER; } } else { return STATUS_INVALID_PARAMETER; } RtlZeroMemory(&lookupEntryBuffer, sizeof(lookupEntryBuffer)); lookupEntryBuffer.SnapshotGuid = ControlItem->SnapshotGuid; KeWaitForSingleObject(&Filter->Root->LookupTableMutex, Executive, KernelMode, FALSE, NULL); lookupEntry = (PVSP_LOOKUP_TABLE_ENTRY) RtlLookupElementGenericTable( &Filter->Root->PersistentSnapshotLookupTable, &lookupEntryBuffer); if (!lookupEntry) { lookupEntry = (PVSP_LOOKUP_TABLE_ENTRY) RtlInsertElementGenericTable( &Filter->Root->PersistentSnapshotLookupTable, &lookupEntryBuffer, sizeof(lookupEntryBuffer), NULL); if (!lookupEntry) { KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); return STATUS_INSUFFICIENT_RESOURCES; } } if (ControlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT) { if (lookupEntry->SnapshotFilter) { KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); return STATUS_UNSUCCESSFUL; } lookupEntry->SnapshotFilter = Filter; lookupEntry->VolumeSnapshotSize = ControlItem->VolumeSnapshotSize; lookupEntry->SnapshotOrderNumber = ControlItem->SnapshotOrderNumber; lookupEntry->SnapshotControlItemFlags = ControlItem->SnapshotControlItemFlags; lookupEntry->SnapshotTime = ControlItem->SnapshotTime; for (l = Filter->SnapshotLookupTableEntries.Flink; l != &Filter->SnapshotLookupTableEntries; l = l->Flink) { le = CONTAINING_RECORD(l, VSP_LOOKUP_TABLE_ENTRY, SnapshotFilterListEntry); if (le->SnapshotOrderNumber > lookupEntry->SnapshotOrderNumber) { break; } } InsertTailList(l, &lookupEntry->SnapshotFilterListEntry); if (lookupEntry->DiffAreaFilter && IsComplete) { *IsComplete = TRUE; } } else { ASSERT(ControlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_DIFF_AREA); if (lookupEntry->DiffAreaFilter) { KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); return STATUS_UNSUCCESSFUL; } lookupEntry->DiffAreaFilter = Filter; lookupEntry->DiffAreaStartingVolumeOffset = ((PVSP_CONTROL_ITEM_DIFF_AREA) ControlItem)-> DiffAreaStartingVolumeOffset; lookupEntry->ApplicationInfoStartingVolumeOffset = ((PVSP_CONTROL_ITEM_DIFF_AREA) ControlItem)-> ApplicationInfoStartingVolumeOffset; lookupEntry->DiffAreaLocationDescriptionVolumeOffset = ((PVSP_CONTROL_ITEM_DIFF_AREA) ControlItem)-> DiffAreaLocationDescriptionVolumeOffset; lookupEntry->InitialBitmapVolumeOffset = ((PVSP_CONTROL_ITEM_DIFF_AREA) ControlItem)-> InitialBitmapVolumeOffset; InsertTailList(&Filter->DiffAreaLookupTableEntries, &lookupEntry->DiffAreaFilterListEntry); if (lookupEntry->SnapshotFilter && IsComplete) { *IsComplete = TRUE; } } KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); return STATUS_SUCCESS; } VOID VspRemoveControlItemInfoFromLookupTable( IN PFILTER_EXTENSION Filter, IN PVSP_CONTROL_ITEM_SNAPSHOT ControlItem ) { VSP_LOOKUP_TABLE_ENTRY lookupEntryBuffer; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; lookupEntryBuffer.SnapshotGuid = ControlItem->SnapshotGuid; KeWaitForSingleObject(&Filter->Root->LookupTableMutex, Executive, KernelMode, FALSE, NULL); lookupEntry = (PVSP_LOOKUP_TABLE_ENTRY) RtlLookupElementGenericTable( &Filter->Root->PersistentSnapshotLookupTable, &lookupEntryBuffer); if (!lookupEntry) { KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); return; } if (ControlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT) { ASSERT(lookupEntry->SnapshotFilter == Filter); lookupEntry->SnapshotFilter = NULL; RemoveEntryList(&lookupEntry->SnapshotFilterListEntry); } else { ASSERT(ControlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_DIFF_AREA); ASSERT(lookupEntry->DiffAreaFilter == Filter); lookupEntry->DiffAreaFilter = NULL; RemoveEntryList(&lookupEntry->DiffAreaFilterListEntry); if (lookupEntry->DiffAreaHandle) { ZwClose(lookupEntry->DiffAreaHandle); lookupEntry->DiffAreaHandle = NULL; } } if (!lookupEntry->SnapshotFilter && !lookupEntry->DiffAreaFilter) { ASSERT(!lookupEntry->DiffAreaHandle); RtlDeleteElementGenericTable( &Filter->Root->PersistentSnapshotLookupTable, lookupEntry); } KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); } PVSP_LOOKUP_TABLE_ENTRY VspFindLookupTableItem( IN PDO_EXTENSION RootExtension, IN GUID* SnapshotGuid ) { VSP_LOOKUP_TABLE_ENTRY lookupEntryBuffer; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; lookupEntryBuffer.SnapshotGuid = *SnapshotGuid; KeWaitForSingleObject(&RootExtension->LookupTableMutex, Executive, KernelMode, FALSE, NULL); lookupEntry = (PVSP_LOOKUP_TABLE_ENTRY) RtlLookupElementGenericTable( &RootExtension->PersistentSnapshotLookupTable, &lookupEntryBuffer); KeReleaseMutex(&RootExtension->LookupTableMutex, FALSE); return lookupEntry; } VOID VspCloseHandlesWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->CloseHandles.Filter; ASSERT(context->Type == VSP_CONTEXT_TYPE_CLOSE_HANDLES); if (context->CloseHandles.Handle1) { ZwClose(context->CloseHandles.Handle1); } if (context->CloseHandles.Handle2) { ZwClose(context->CloseHandles.Handle2); } KeSetEvent(&filter->ControlBlockFileHandleReady, IO_NO_INCREMENT, FALSE); VspFreeContext(filter->Root, context); ObDereferenceObject(filter->DeviceObject); } NTSTATUS VspDeleteControlItemsWithGuid( IN PFILTER_EXTENSION Filter, IN GUID* SnapshotGuid, IN BOOLEAN NonPagedResourceHeld ) /*++ Routine Description: This routine adds a control item to the given master control block on the given filter. Arguments: Filter - Supplies the filter extension. ControlItem - Supplies the control item. Return Value: NTSTATUS --*/ { PVSP_BLOCK_CONTROL controlBlock; LONGLONG byteOffset; NTSTATUS status; BOOLEAN needWrite, itemsLeft; ULONG offset; PVSP_CONTROL_ITEM_SNAPSHOT controlItem; HANDLE h, hh; PVSP_CONTEXT context; if (!NonPagedResourceHeld) { VspAcquireNonPagedResource(Filter, NULL, FALSE); } if (!Filter->FirstControlBlockVolumeOffset) { if (!NonPagedResourceHeld) { VspReleaseNonPagedResource(Filter); } return STATUS_SUCCESS; } ASSERT(Filter->SnapshotOnDiskIrp); controlBlock = (PVSP_BLOCK_CONTROL) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); byteOffset = Filter->FirstControlBlockVolumeOffset; itemsLeft = FALSE; for (;;) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, byteOffset, 0); if (!NT_SUCCESS(status)) { if (!NonPagedResourceHeld) { VspReleaseNonPagedResource(Filter); } return status; } needWrite = FALSE; for (offset = VSP_BYTES_PER_CONTROL_ITEM; offset < BLOCK_SIZE; offset += VSP_BYTES_PER_CONTROL_ITEM) { controlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) ((PCHAR) controlBlock + offset); if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_END) { break; } if (controlItem->ControlItemType > VSP_CONTROL_ITEM_TYPE_FREE) { if (SnapshotGuid) { if (IsEqualGUID(controlItem->SnapshotGuid, *SnapshotGuid)) { VspRemoveControlItemInfoFromLookupTable( Filter, controlItem); RtlZeroMemory(controlItem, VSP_BYTES_PER_CONTROL_ITEM); controlItem->ControlItemType = VSP_CONTROL_ITEM_TYPE_FREE; needWrite = TRUE; } else { itemsLeft = TRUE; } } else { if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT) { VspRemoveControlItemInfoFromLookupTable( Filter, controlItem); RtlZeroMemory(controlItem, VSP_BYTES_PER_CONTROL_ITEM); controlItem->ControlItemType = VSP_CONTROL_ITEM_TYPE_FREE; needWrite = TRUE; } else { itemsLeft = TRUE; } } } } if (needWrite) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, byteOffset, 0); if (!NT_SUCCESS(status)) { if (!NonPagedResourceHeld) { VspReleaseNonPagedResource(Filter); } return status; } } if (!controlBlock->Header.NextVolumeOffset) { break; } byteOffset = controlBlock->Header.NextVolumeOffset; } if (!itemsLeft) { if (NonPagedResourceHeld) { context = VspAllocateContext(Filter->Root); if (context) { h = InterlockedExchangePointer(&Filter->ControlBlockFileHandle, NULL); Filter->FirstControlBlockVolumeOffset = 0; VspCreateStartBlock(Filter, 0, Filter->MaximumVolumeSpace); hh = InterlockedExchangePointer(&Filter->BootStatHandle, NULL); if (!h && !hh) { VspFreeContext(Filter->Root, context); } } else { h = NULL; hh = NULL; } } else { h = InterlockedExchangePointer(&Filter->ControlBlockFileHandle, NULL); Filter->FirstControlBlockVolumeOffset = 0; VspCreateStartBlock(Filter, 0, Filter->MaximumVolumeSpace); hh = InterlockedExchangePointer(&Filter->BootStatHandle, NULL); } } else { h = NULL; hh = NULL; } if (NonPagedResourceHeld) { if (h || hh) { ASSERT(context); context->Type = VSP_CONTEXT_TYPE_CLOSE_HANDLES; ExInitializeWorkItem(&context->WorkItem, VspCloseHandlesWorker, context); context->CloseHandles.Filter = Filter; context->CloseHandles.Handle1 = h; context->CloseHandles.Handle2 = hh; ObReferenceObject(Filter->DeviceObject); KeResetEvent(&Filter->ControlBlockFileHandleReady); ExQueueWorkItem(&context->WorkItem, CriticalWorkQueue); } } else { VspReleaseNonPagedResource(Filter); if (h) { ZwClose(h); } if (hh) { ZwClose(hh); } } return STATUS_SUCCESS; } NTSTATUS VspIoControlItem( IN PFILTER_EXTENSION Filter, IN ULONG ControlItemType, IN GUID* SnapshotGuid, IN BOOLEAN IsSet, IN OUT PVOID ControlItem, IN BOOLEAN AcquireLock ) /*++ Routine Description: This routine returns the control item with the given control item type and snapshot guid. Arguments: Filter - Supplies the filter. ControlItemType - Supplies the control item type. SnapshotGuid - Supplies the snapshot guid. ControlItem - Returns the control item. Return Value: NTSTATUS --*/ { PVSP_BLOCK_CONTROL controlBlock; LONGLONG byteOffset; NTSTATUS status; ULONG offset; PVSP_CONTROL_ITEM_SNAPSHOT controlItem; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; PVSP_CONTROL_ITEM_DIFF_AREA daControlItem; if (AcquireLock) { VspAcquireNonPagedResource(Filter, NULL, FALSE); } if (!Filter->FirstControlBlockVolumeOffset) { if (AcquireLock) { VspReleaseNonPagedResource(Filter); } if (!IsSet) { return STATUS_UNSUCCESSFUL; } status = VspCreateInitialControlBlockFile(Filter); if (AcquireLock) { VspAcquireNonPagedResource(Filter, NULL, FALSE); } if (!Filter->FirstControlBlockVolumeOffset) { if (AcquireLock) { VspReleaseNonPagedResource(Filter); } if (NT_SUCCESS(status)) { status = STATUS_UNSUCCESSFUL; } return status; } } ASSERT(Filter->SnapshotOnDiskIrp); controlBlock = (PVSP_BLOCK_CONTROL) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); byteOffset = Filter->FirstControlBlockVolumeOffset; for (;;) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, byteOffset, 0); if (!NT_SUCCESS(status)) { if (AcquireLock) { VspReleaseNonPagedResource(Filter); } return status; } for (offset = VSP_BYTES_PER_CONTROL_ITEM; offset < BLOCK_SIZE; offset += VSP_BYTES_PER_CONTROL_ITEM) { controlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) ((PCHAR) controlBlock + offset); if (ControlItemType == VSP_CONTROL_ITEM_TYPE_FREE) { if (controlItem->ControlItemType > VSP_CONTROL_ITEM_TYPE_FREE) { continue; } } else { if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_END) { break; } if (controlItem->ControlItemType != ControlItemType || !IsEqualGUID(controlItem->SnapshotGuid, *SnapshotGuid)) { continue; } } if (!IsSet) { RtlCopyMemory(ControlItem, controlItem, VSP_BYTES_PER_CONTROL_ITEM); if (AcquireLock) { VspReleaseNonPagedResource(Filter); } return STATUS_SUCCESS; } RtlCopyMemory(controlItem, ControlItem, VSP_BYTES_PER_CONTROL_ITEM); if (ControlItemType == VSP_CONTROL_ITEM_TYPE_FREE) { status = VspAddControlItemInfoToLookupTable(Filter, controlItem, NULL); if (!NT_SUCCESS(status)) { if (AcquireLock) { VspReleaseNonPagedResource(Filter); } return status; } } else if (ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT) { ASSERT(controlItem->ControlItemType == ControlItemType); lookupEntry = VspFindLookupTableItem( Filter->Root, &controlItem->SnapshotGuid); ASSERT(lookupEntry); lookupEntry->VolumeSnapshotSize = controlItem->VolumeSnapshotSize; lookupEntry->SnapshotOrderNumber = controlItem->SnapshotOrderNumber; lookupEntry->SnapshotControlItemFlags = controlItem->SnapshotControlItemFlags; lookupEntry->SnapshotTime = controlItem->SnapshotTime; } else { ASSERT(controlItem->ControlItemType == ControlItemType); lookupEntry = VspFindLookupTableItem( Filter->Root, &controlItem->SnapshotGuid); ASSERT(lookupEntry); daControlItem = (PVSP_CONTROL_ITEM_DIFF_AREA) controlItem; lookupEntry->DiffAreaStartingVolumeOffset = daControlItem->DiffAreaStartingVolumeOffset; lookupEntry->ApplicationInfoStartingVolumeOffset = daControlItem->ApplicationInfoStartingVolumeOffset; lookupEntry->DiffAreaLocationDescriptionVolumeOffset = daControlItem->DiffAreaLocationDescriptionVolumeOffset; lookupEntry->InitialBitmapVolumeOffset = daControlItem->InitialBitmapVolumeOffset; } status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, byteOffset, 0); if (!NT_SUCCESS(status)) { VspRemoveControlItemInfoFromLookupTable(Filter, controlItem); if (AcquireLock) { VspReleaseNonPagedResource(Filter); } return status; } if (AcquireLock) { VspReleaseNonPagedResource(Filter); } return STATUS_SUCCESS; } if (!controlBlock->Header.NextVolumeOffset) { if (ControlItemType == VSP_CONTROL_ITEM_TYPE_FREE) { if (AcquireLock) { VspReleaseNonPagedResource(Filter); } status = VspAddControlBlocks(Filter, byteOffset); if (!NT_SUCCESS(status)) { return status; } byteOffset = Filter->FirstControlBlockVolumeOffset; if (AcquireLock) { VspAcquireNonPagedResource(Filter, NULL, FALSE); } continue; } break; } byteOffset = controlBlock->Header.NextVolumeOffset; } if (AcquireLock) { VspReleaseNonPagedResource(Filter); } return STATUS_UNSUCCESSFUL; } NTSTATUS VspAddControlItem( IN PFILTER_EXTENSION Filter, IN PVOID ControlItem, IN BOOLEAN AcquireLock ) /*++ Routine Description: This routine adds a control item to the given master control block on the given filter. Arguments: Filter - Supplies the filter extension. ControlItem - Supplies the control item. Return Value: NTSTATUS --*/ { GUID zeroGuid; RtlZeroMemory(&zeroGuid, sizeof(zeroGuid)); return VspIoControlItem(Filter, VSP_CONTROL_ITEM_TYPE_FREE, &zeroGuid, TRUE, ControlItem, AcquireLock); } NTSTATUS VspCleanupControlItemsForSnapshot( IN PVOLUME_EXTENSION Extension ) /*++ Routine Description: This routine cleans up all of the control items associated with the given snapshot. Arguments: Extension - Supplies the volume extension. Return Value: NTSTATUS --*/ { NTSTATUS status; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; status = VspDeleteControlItemsWithGuid(Extension->Filter, &Extension->SnapshotGuid, FALSE); if (!NT_SUCCESS(status)) { return status; } if (Extension->DiffAreaFile) { diffAreaFile = Extension->DiffAreaFile; status = VspDeleteControlItemsWithGuid(diffAreaFile->Filter, &Extension->SnapshotGuid, FALSE); if (!NT_SUCCESS(status)) { return status; } } return STATUS_SUCCESS; } NTSTATUS VspLayDownOnDiskForSnapshot( IN PVOLUME_EXTENSION Extension ) /*++ Routine Description: This routine lays down the on disk structure for the given snapshot, including the snapshot control item, diff area control items, and the initial diff area file blocks. Arguments: Extension - Supplies the volume extension. Return Value: NTSTATUS --*/ { UCHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; PVSP_CONTROL_ITEM_DIFF_AREA diffAreaControlItem = (PVSP_CONTROL_ITEM_DIFF_AREA) controlItemBuffer; NTSTATUS status; PVSP_BLOCK_DIFF_AREA_LOCATION_DESCRIPTION locationBlock; ULONG totalNumEntries, numEntriesPerBlock, numBlocks, j; PLIST_ENTRY l, ll; PVSP_DIFF_AREA_FILE diffAreaFile; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; IO_STATUS_BLOCK ioStatus; KIRQL irql; LONGLONG targetOffset, fileOffset, fo; PLONGLONG targetOffsetArray, fileOffsetArray; ULONG blockOffset; PVSP_DIFF_AREA_LOCATION_DESCRIPTOR locationDescriptor; PVSP_BLOCK_APP_INFO appInfoBlock; RtlZeroMemory(controlItemBuffer, VSP_BYTES_PER_CONTROL_ITEM); snapshotControlItem->ControlItemType = VSP_CONTROL_ITEM_TYPE_SNAPSHOT; snapshotControlItem->VolumeSnapshotSize = Extension->VolumeSize; snapshotControlItem->SnapshotGuid = Extension->SnapshotGuid; status = VspAddControlItem(Extension->Filter, snapshotControlItem, TRUE); if (!NT_SUCCESS(status)) { return status; } RtlZeroMemory(controlItemBuffer, VSP_BYTES_PER_CONTROL_ITEM); diffAreaControlItem->ControlItemType = VSP_CONTROL_ITEM_TYPE_DIFF_AREA; diffAreaControlItem->SnapshotGuid = Extension->SnapshotGuid; diffAreaFile = Extension->DiffAreaFile; ASSERT(diffAreaFile); totalNumEntries = 0; for (ll = diffAreaFile->UnusedAllocationList.Flink; ll != &diffAreaFile->UnusedAllocationList; ll = ll->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(ll, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength > 0) { totalNumEntries++; } } numEntriesPerBlock = (BLOCK_SIZE - VSP_OFFSET_TO_FIRST_LOCATION_DESCRIPTOR)/ sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR); numBlocks = (totalNumEntries + numEntriesPerBlock - 1)/ numEntriesPerBlock; numBlocks += 2; locationBlock = (PVSP_BLOCK_DIFF_AREA_LOCATION_DESCRIPTION) MmGetMdlVirtualAddress(diffAreaFile->TableUpdateIrp->MdlAddress); targetOffsetArray = (PLONGLONG) ExAllocatePoolWithTag(PagedPool, sizeof(LONGLONG)*numBlocks, VOLSNAP_TAG_SHORT_TERM); fileOffsetArray = (PLONGLONG) ExAllocatePoolWithTag(PagedPool, sizeof(LONGLONG)*numBlocks, VOLSNAP_TAG_SHORT_TERM); if (!targetOffsetArray || !fileOffsetArray) { if (targetOffsetArray) { ExFreePool(targetOffsetArray); } if (fileOffsetArray) { ExFreePool(fileOffsetArray); } VspCleanupControlItemsForSnapshot(Extension); return STATUS_INSUFFICIENT_RESOURCES; } for (j = 0; j < numBlocks; j++) { status = VspAllocateDiffAreaSpace(Extension, &targetOffset, &fileOffset, NULL, NULL); if (!NT_SUCCESS(status)) { ExFreePool(targetOffsetArray); ExFreePool(fileOffsetArray); VspCleanupControlItemsForSnapshot(Extension); return STATUS_INSUFFICIENT_RESOURCES; } targetOffsetArray[j] = targetOffset; fileOffsetArray[j] = fileOffset; } diffAreaFile->NextFreeTableEntryOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; diffAreaFile->ApplicationInfoTargetOffset = targetOffsetArray[0]; diffAreaFile->FirstTableTargetOffset = targetOffsetArray[1]; diffAreaFile->TableTargetOffset = targetOffsetArray[1]; diffAreaFile->DiffAreaLocationDescriptionTargetOffset = targetOffsetArray[2]; ll = diffAreaFile->UnusedAllocationList.Flink; fo = diffAreaFile->NextAvailable; for (j = 0; j < numBlocks; j++) { RtlZeroMemory(locationBlock, BLOCK_SIZE); locationBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; locationBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; locationBlock->Header.ThisFileOffset = fileOffsetArray[j]; locationBlock->Header.ThisVolumeOffset = targetOffsetArray[j]; if (j == 0) { locationBlock->Header.BlockType = VSP_BLOCK_TYPE_APP_INFO; if (Extension->ApplicationInformationSize) { appInfoBlock = (PVSP_BLOCK_APP_INFO) locationBlock; appInfoBlock->AppInfoSize = Extension->ApplicationInformationSize; RtlCopyMemory((PCHAR) locationBlock + VSP_OFFSET_TO_APP_INFO, Extension->ApplicationInformation, Extension->ApplicationInformationSize); } status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, locationBlock->Header.ThisVolumeOffset, 0); if (!NT_SUCCESS(status)) { ExFreePool(targetOffsetArray); ExFreePool(fileOffsetArray); VspCleanupControlItemsForSnapshot(Extension); return status; } continue; } if (j == 1) { locationBlock->Header.BlockType = VSP_BLOCK_TYPE_DIFF_AREA; status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, locationBlock->Header.ThisVolumeOffset, 0); if (!NT_SUCCESS(status)) { ExFreePool(targetOffsetArray); ExFreePool(fileOffsetArray); VspCleanupControlItemsForSnapshot(Extension); return status; } continue; } locationBlock->Header.BlockType = VSP_BLOCK_TYPE_DIFF_AREA_LOCATION_DESCRIPTION; if (j + 1 < numBlocks) { locationBlock->Header.NextVolumeOffset = targetOffsetArray[j + 1]; } blockOffset = VSP_OFFSET_TO_FIRST_LOCATION_DESCRIPTOR; locationDescriptor = (PVSP_DIFF_AREA_LOCATION_DESCRIPTOR) ((PCHAR) locationBlock + blockOffset); for (; ll != &diffAreaFile->UnusedAllocationList; ll = ll->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(ll, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength <= 0) { fo -= diffAreaFileAllocation->NLength; continue; } locationDescriptor->VolumeOffset = diffAreaFileAllocation->Offset; locationDescriptor->FileOffset = fo; locationDescriptor->Length = diffAreaFileAllocation->NLength; fo += locationDescriptor->Length; blockOffset += sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR); if (blockOffset + sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR) > BLOCK_SIZE) { ll = ll->Flink; break; } locationDescriptor = (PVSP_DIFF_AREA_LOCATION_DESCRIPTOR) ((PCHAR) locationBlock + blockOffset); } status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, locationBlock->Header.ThisVolumeOffset, 0); if (!NT_SUCCESS(status)) { ExFreePool(targetOffsetArray); ExFreePool(fileOffsetArray); VspCleanupControlItemsForSnapshot(Extension); return status; } } ExFreePool(targetOffsetArray); ExFreePool(fileOffsetArray); status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffAreaFile->Filter->TargetObject, IRP_MJ_READ, diffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { VspCleanupControlItemsForSnapshot(Extension); return status; } diffAreaControlItem->DiffAreaStartingVolumeOffset = diffAreaFile->TableTargetOffset; diffAreaControlItem->ApplicationInfoStartingVolumeOffset = diffAreaFile->ApplicationInfoTargetOffset; diffAreaControlItem->DiffAreaLocationDescriptionVolumeOffset = diffAreaFile->DiffAreaLocationDescriptionTargetOffset; diffAreaControlItem->InitialBitmapVolumeOffset = diffAreaFile->InitialBitmapVolumeOffset; status = VspAddControlItem(diffAreaFile->Filter, diffAreaControlItem, TRUE); if (!NT_SUCCESS(status)) { VspCleanupControlItemsForSnapshot(Extension); return status; } return STATUS_SUCCESS; } HANDLE VspPinBootStat( IN PFILTER_EXTENSION Filter ) { UNICODE_STRING name; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE h; IO_STATUS_BLOCK ioStatus; RtlInitUnicodeString(&name, L"\\SystemRoot\\bootstat.dat"); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { return NULL; } status = VspPinFile(Filter->TargetObject, h); if (!NT_SUCCESS(status)) { ZwClose(h); return NULL; } return h; } BOOLEAN VspQueryNoDiffAreaFill( IN PDO_EXTENSION RootExtension ) { ULONG zero, result; RTL_QUERY_REGISTRY_TABLE queryTable[2]; NTSTATUS status; zero = 0; RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"NoDiffAreaFill"; queryTable[0].EntryContext = &result; queryTable[0].DefaultType = REG_DWORD; queryTable[0].DefaultData = &zero; queryTable[0].DefaultLength = sizeof(ULONG); status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RootExtension->RegistryPath.Buffer, queryTable, NULL, NULL); if (!NT_SUCCESS(status)) { result = zero; } return result ? TRUE : FALSE; } VOID VspPerformPreExposure( IN PVOLUME_EXTENSION Extension ) { NTSTATUS status; LARGE_INTEGER timeout; VspAcquire(Extension->Root); if (Extension->Filter->IsRemoved) { VspRelease(Extension->Root); return; } IoInvalidateDeviceRelations(Extension->Filter->Pdo, BusRelations); VspRelease(Extension->Root); timeout.QuadPart = (LONGLONG) -10*1000*1000*120*10; // 20 minutes. status = KeWaitForSingleObject(&Extension->PreExposureEvent, Executive, KernelMode, FALSE, &timeout); if (status == STATUS_TIMEOUT) { VspAcquire(Extension->Root); if (Extension->IsStarted) { Extension->IsInstalled = TRUE; } VspRelease(Extension->Root); } } ULONG VspClaimNextDevnodeNumber( IN PDO_EXTENSION RootExtension ) { PULONG p, c; ULONG r; p = NULL; c = (PULONG) RtlEnumerateGenericTable(&RootExtension->UsedDevnodeNumbers, TRUE); while (c) { if (p) { if (*p + 1 < *c) { r = *p + 1; goto Finish; } } else { if (1 < *c) { r = 1; goto Finish; } } p = c; c = (PULONG) RtlEnumerateGenericTable(&RootExtension->UsedDevnodeNumbers, FALSE); } if (p) { r = *p + 1; } else { r = 1; } Finish: if (!RtlInsertElementGenericTable(&RootExtension->UsedDevnodeNumbers, &r, sizeof(r), NULL)) { return 0; } return r; } VOID VspPrepareForSnapshotWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION Filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIRP Irp = context->Dispatch.Irp; PDO_EXTENSION rootExtension = Filter->Root; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_PREPARE_INFO input = (PVOLSNAP_PREPARE_INFO) Irp->AssociatedIrp.SystemBuffer; BOOLEAN fallThrough = FALSE; BOOLEAN isInternal, isPersistent; PULONG pu; LONGLONG minDiffAreaFileSize; KIRQL irql; ULONG volumeNumber; WCHAR buffer[100]; UNICODE_STRING volumeName; NTSTATUS status; PDEVICE_OBJECT deviceObject; PVOLUME_EXTENSION extension, e; ULONG bitmapSize, n; PVOID bitmapBuffer, p; PVOID buf; PMDL mdl; HANDLE h, hh; PLIST_ENTRY l; PSECURITY_DESCRIPTOR sd; BOOLEAN ma; ASSERT(context->Type == VSP_CONTEXT_TYPE_DISPATCH); InterlockedExchange(&rootExtension->VolumesSafeForWriteAccess, TRUE); KeWaitForSingleObject(&Filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); VspAcquireCritical(Filter); if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLSNAP_PREPARE_INFO)) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; goto Finish; } if (irpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(VOLSNAP_PREPARE_INFO) + sizeof(ULONG)) { pu = (PULONG) (input + 1); if (*pu == 'NPK ') { isInternal = TRUE; } else { isInternal = FALSE; } } else { isInternal = FALSE; } if (input->Attributes&(~VOLSNAP_ALL_ATTRIBUTES)) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; goto Finish; } if (input->Attributes&VOLSNAP_ATTRIBUTE_PERSISTENT) { isPersistent = TRUE; } else { isPersistent = FALSE; } KeWaitForSingleObject(&Filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); VspAcquire(Filter->Root); if (!Filter->DiffAreaVolume) { VspRelease(Filter->Root); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; goto Finish; } if ((isPersistent && Filter->ProtectedBlocksBitmap) || Filter->DiffAreaVolume->ProtectedBlocksBitmap) { VspRelease(Filter->Root); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; goto Finish; } if (Filter->SnapshotsPresent) { if (Filter->PersistentSnapshots) { isPersistent = TRUE; } } if (isPersistent && Filter->Pdo->Flags&DO_SYSTEM_BOOT_PARTITION && !Filter->BootStatHandle) { VspRelease(Filter->Root); h = VspPinBootStat(Filter); VspAcquire(Filter->Root); if (h) { hh = InterlockedExchangePointer(&Filter->BootStatHandle, h); if (hh) { ZwClose(hh); } } } VspRelease(Filter->Root); VspQueryMinimumDiffAreaFileSize(Filter->Root, &minDiffAreaFileSize); if (input->InitialDiffAreaAllocation < 2*NOMINAL_DIFF_AREA_FILE_GROWTH) { input->InitialDiffAreaAllocation = 2*NOMINAL_DIFF_AREA_FILE_GROWTH; } if (input->InitialDiffAreaAllocation < minDiffAreaFileSize) { input->InitialDiffAreaAllocation = minDiffAreaFileSize; } volumeNumber = (ULONG) InterlockedIncrement(&Filter->Root->NextVolumeNumber); swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", volumeNumber); RtlInitUnicodeString(&volumeName, buffer); status = IoCreateDevice(rootExtension->DriverObject, sizeof(VOLUME_EXTENSION), &volumeName, FILE_DEVICE_VIRTUAL_DISK, 0, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { Irp->IoStatus.Status = status; goto Finish; } extension = (PVOLUME_EXTENSION) deviceObject->DeviceExtension; RtlZeroMemory(extension, sizeof(VOLUME_EXTENSION)); extension->DeviceObject = deviceObject; extension->Root = Filter->Root; extension->DeviceExtensionType = DEVICE_EXTENSION_VOLUME; KeInitializeSpinLock(&extension->SpinLock); extension->Filter = Filter; extension->RefCount = 1; InitializeListHead(&extension->WriteContextList); KeInitializeEvent(&extension->ZeroRefEvent, NotificationEvent, FALSE); extension->IsPersistent = isPersistent; extension->NoDiffAreaFill = VspQueryNoDiffAreaFill(extension->Root); if (isPersistent) { extension->OnDiskNotCommitted = TRUE; } extension->VolumeNumber = volumeNumber; status = ExUuidCreate(&extension->SnapshotGuid); if (!NT_SUCCESS(status)) { IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } RtlInitializeGenericTable(&extension->VolumeBlockTable, VspTableCompareRoutine, VspTableAllocateRoutine, VspTableFreeRoutine, extension); RtlInitializeGenericTable(&extension->CopyBackPointerTable, VspTableCompareRoutine, VspTableAllocateRoutine, VspTableFreeRoutine, extension); RtlInitializeGenericTable(&extension->TempVolumeBlockTable, VspTableCompareRoutine, VspTempTableAllocateRoutine, VspTempTableFreeRoutine, extension); extension->DiffAreaFileIncrease = NOMINAL_DIFF_AREA_FILE_GROWTH; status = VspCreateInitialDiffAreaFile(extension, input->InitialDiffAreaAllocation); if (!NT_SUCCESS(status)) { IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } status = VspCreateWorkerThread(rootExtension); if (!NT_SUCCESS(status)) { VspLogError(extension, NULL, VS_CREATE_WORKER_THREADS_FAILED, status, 0, FALSE); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } extension->VolumeBlockBitmap = (PRTL_BITMAP) ExAllocatePoolWithTag( NonPagedPool, sizeof(RTL_BITMAP), VOLSNAP_TAG_BITMAP); if (!extension->VolumeBlockBitmap) { VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } extension->ApplicationInformationSize = 68; extension->ApplicationInformation = ExAllocatePoolWithTag(PagedPool, extension->ApplicationInformationSize, VOLSNAP_TAG_APP_INFO); if (!extension->ApplicationInformation) { ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } RtlZeroMemory(extension->ApplicationInformation, extension->ApplicationInformationSize); RtlCopyMemory(extension->ApplicationInformation, &VOLSNAP_APPINFO_GUID_SYSTEM_HIDDEN, sizeof(GUID)); extension->VolumeSize = VspQueryVolumeSize(Filter); if (!extension->VolumeSize) { ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } bitmapSize = (ULONG) ((extension->VolumeSize + BLOCK_SIZE - 1)>> BLOCK_SHIFT); bitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { VspLogError(extension, NULL, VS_CANT_ALLOCATE_BITMAP, STATUS_INSUFFICIENT_RESOURCES, 1, FALSE); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } RtlInitializeBitMap(extension->VolumeBlockBitmap, (PULONG) bitmapBuffer, bitmapSize); RtlClearAllBits(extension->VolumeBlockBitmap); status = VspCreateInitialHeap(extension, FALSE); if (!NT_SUCCESS(status)) { ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } InitializeListHead(&extension->OldHeaps); extension->EmergencyCopyIrp = IoAllocateIrp((CCHAR) extension->Root->StackSize, FALSE); if (!extension->EmergencyCopyIrp) { status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } buf = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buf) { IoFreeIrp(extension->EmergencyCopyIrp); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } mdl = IoAllocateMdl(buf, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buf); IoFreeIrp(extension->EmergencyCopyIrp); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } MmBuildMdlForNonPagedPool(mdl); extension->EmergencyCopyIrp->MdlAddress = mdl; InitializeListHead(&extension->EmergencyCopyIrpQueue); InitializeListHead(&extension->WaitingForPageFileSpace); InitializeListHead(&extension->WaitingForDiffAreaSpace); VspAcquire(rootExtension); if (!IsListEmpty(&Filter->VolumeList)) { extension->IgnorableProduct = (PRTL_BITMAP) ExAllocatePoolWithTag(NonPagedPool, sizeof(RTL_BITMAP), VOLSNAP_TAG_BITMAP); if (!extension->IgnorableProduct) { VspRelease(rootExtension); ExFreePool(buf); IoFreeIrp(extension->EmergencyCopyIrp); IoFreeMdl(mdl); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } p = ExAllocatePoolWithTag(NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!p) { VspLogError(extension, NULL, VS_CANT_ALLOCATE_BITMAP, STATUS_INSUFFICIENT_RESOURCES, 2, FALSE); ExFreePool(extension->IgnorableProduct); extension->IgnorableProduct = NULL; VspRelease(rootExtension); ExFreePool(buf); IoFreeMdl(mdl); IoFreeIrp(extension->EmergencyCopyIrp); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } RtlInitializeBitMap(extension->IgnorableProduct, (PULONG) p, bitmapSize); RtlSetAllBits(extension->IgnorableProduct); e = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); KeAcquireSpinLock(&e->SpinLock, &irql); if (e->VolumeBlockBitmap) { n = extension->IgnorableProduct->SizeOfBitMap; extension->IgnorableProduct->SizeOfBitMap = e->VolumeBlockBitmap->SizeOfBitMap; VspAndBitmaps(extension->IgnorableProduct, e->VolumeBlockBitmap); extension->IgnorableProduct->SizeOfBitMap = n; } KeReleaseSpinLock(&e->SpinLock, irql); } status = VspSetDiffAreaBlocksInBitmap(extension); if (!NT_SUCCESS(status)) { if (extension->IgnorableProduct) { ExFreePool(extension->IgnorableProduct->Buffer); ExFreePool(extension->IgnorableProduct); extension->IgnorableProduct = NULL; } VspRelease(rootExtension); ExFreePool(buf); IoFreeMdl(mdl); IoFreeIrp(extension->EmergencyCopyIrp); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } status = ObGetObjectSecurity(Filter->Pdo, &sd, &ma); if (NT_SUCCESS(status)) { status = ObSetSecurityObjectByPointer(deviceObject, DACL_SECURITY_INFORMATION, sd); ObReleaseObjectSecurity(sd, ma); } if (NT_SUCCESS(status)) { extension->DevnodeNumber = VspClaimNextDevnodeNumber(extension->Root); if (!extension->DevnodeNumber) { status = STATUS_INSUFFICIENT_RESOURCES; } } if (!NT_SUCCESS(status)) { if (extension->DiffAreaFile) { KeAcquireSpinLock(&extension->DiffAreaFile->Filter->SpinLock, &irql); if (extension->DiffAreaFile->FilterListEntryBeingUsed) { RemoveEntryList(&extension->DiffAreaFile->FilterListEntry); extension->DiffAreaFile->FilterListEntryBeingUsed = FALSE; } KeReleaseSpinLock(&extension->DiffAreaFile->Filter->SpinLock, irql); } if (extension->IgnorableProduct) { ExFreePool(extension->IgnorableProduct->Buffer); ExFreePool(extension->IgnorableProduct); extension->IgnorableProduct = NULL; } VspRelease(rootExtension); ExFreePool(buf); IoFreeMdl(mdl); IoFreeIrp(extension->EmergencyCopyIrp); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); RtlDeleteElementGenericTable(&rootExtension->UsedDevnodeNumbers, &extension->DevnodeNumber); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } if (extension->IsPersistent) { VspRelease(rootExtension); status = VspLayDownOnDiskForSnapshot(extension); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_CANT_LAY_DOWN_ON_DISK, status, 0, FALSE); VspAcquire(rootExtension); if (extension->DiffAreaFile) { KeAcquireSpinLock(&extension->DiffAreaFile->Filter->SpinLock, &irql); if (extension->DiffAreaFile->FilterListEntryBeingUsed) { RemoveEntryList(&extension->DiffAreaFile->FilterListEntry); extension->DiffAreaFile->FilterListEntryBeingUsed = FALSE; } KeReleaseSpinLock(&extension->DiffAreaFile->Filter->SpinLock, irql); } VspRelease(rootExtension); if (extension->IgnorableProduct) { ExFreePool(extension->IgnorableProduct->Buffer); ExFreePool(extension->IgnorableProduct); extension->IgnorableProduct = NULL; } ExFreePool(buf); IoFreeIrp(extension->EmergencyCopyIrp); ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ExFreePool(bitmapBuffer); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspDeleteWorkerThread(rootExtension); VspDeleteInitialDiffAreaFile(extension); RtlDeleteElementGenericTable(&rootExtension->UsedDevnodeNumbers, &extension->DevnodeNumber); IoDeleteDevice(deviceObject); Irp->IoStatus.Status = status; goto Finish; } VspAcquire(rootExtension); } KeAcquireSpinLock(&Filter->SpinLock, &irql); e = Filter->PreparedSnapshot; Filter->PreparedSnapshot = extension; KeReleaseSpinLock(&Filter->SpinLock, irql); if (!isInternal) { ObReferenceObject(extension->DeviceObject); extension->IsPreExposure = TRUE; KeInitializeEvent(&extension->PreExposureEvent, NotificationEvent, FALSE); } deviceObject->Flags |= DO_DIRECT_IO; deviceObject->StackSize = (CCHAR) Filter->Root->StackSize + 1; deviceObject->AlignmentRequirement = extension->Filter->Pdo->AlignmentRequirement | extension->DiffAreaFile->Filter->Pdo->AlignmentRequirement; deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; VspRelease(rootExtension); if (e) { VspCleanupInitialSnapshot(e, TRUE, FALSE); } fallThrough = TRUE; Finish: VspReleaseCritical(Filter); if (fallThrough && !isInternal) { VspPerformPreExposure(extension); ObDereferenceObject(extension->DeviceObject); } IoFreeWorkItem(context->Dispatch.IoWorkItem); VspFreeContext(rootExtension, context); IoCompleteRequest(Irp, IO_NO_INCREMENT); } NTSTATUS VspPrepareForSnapshot( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine prepares a snapshot device object to be used later for a snapshot. This phase is distict from commit snapshot because it can be called before IRPs are held. Besides creating the device object, this routine will also pre allocate some of the diff area. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PVSP_CONTEXT context; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; context = VspAllocateContext(Filter->Root); if (!context) { return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_DISPATCH; context->Dispatch.IoWorkItem = IoAllocateWorkItem(Filter->DeviceObject); if (!context->Dispatch.IoWorkItem) { VspFreeContext(Filter->Root, context); return STATUS_INSUFFICIENT_RESOURCES; } IoMarkIrpPending(Irp); context->Dispatch.Irp = Irp; IoQueueWorkItem(context->Dispatch.IoWorkItem, VspPrepareForSnapshotWorker, DelayedWorkQueue, context); return STATUS_PENDING; } VOID VspCleanupInitialMaps( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; NTSTATUS status; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); VspFreeContext(extension->Root, context); VspReleasePagedResource(extension); ObDereferenceObject(extension->DeviceObject); } VOID VspCleanupInitialSnapshot( IN PVOLUME_EXTENSION Extension, IN BOOLEAN NeedLock, IN BOOLEAN IsFinalRemove ) { PVSP_CONTEXT context; NTSTATUS status; KIRQL irql; if (Extension->IsPersistent) { VspCleanupControlItemsForSnapshot(Extension); } context = VspAllocateContext(Extension->Root); if (context) { context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = Extension; context->Extension.Irp = NULL; ExInitializeWorkItem(&context->WorkItem, VspCleanupInitialMaps, context); ObReferenceObject(Extension->DeviceObject); VspAcquirePagedResource(Extension, &context->WorkItem); } else { status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, Extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status)); status = ZwUnmapViewOfSection(Extension->DiffAreaFileMapProcess, Extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); } if (NeedLock) { VspAcquire(Extension->Root); } VspDeleteWorkerThread(Extension->Root); if (Extension->DiffAreaFile) { KeAcquireSpinLock(&Extension->DiffAreaFile->Filter->SpinLock, &irql); if (Extension->DiffAreaFile->FilterListEntryBeingUsed) { RemoveEntryList(&Extension->DiffAreaFile->FilterListEntry); Extension->DiffAreaFile->FilterListEntryBeingUsed = FALSE; } KeReleaseSpinLock(&Extension->DiffAreaFile->Filter->SpinLock, irql); } VspDeleteInitialDiffAreaFile(Extension); if (Extension->IsPreExposure) { KeSetEvent(&Extension->PreExposureEvent, IO_NO_INCREMENT, FALSE); } if (NeedLock) { VspRelease(Extension->Root); } KeAcquireSpinLock(&Extension->SpinLock, &irql); ExFreePool(Extension->VolumeBlockBitmap->Buffer); ExFreePool(Extension->VolumeBlockBitmap); Extension->VolumeBlockBitmap = NULL; if (Extension->IgnorableProduct) { ExFreePool(Extension->IgnorableProduct->Buffer); ExFreePool(Extension->IgnorableProduct); Extension->IgnorableProduct = NULL; } KeReleaseSpinLock(&Extension->SpinLock, irql); ExFreePool(MmGetMdlVirtualAddress( Extension->EmergencyCopyIrp->MdlAddress)); IoFreeMdl(Extension->EmergencyCopyIrp->MdlAddress); IoFreeIrp(Extension->EmergencyCopyIrp); if (Extension->AliveToPnp) { InsertTailList(&Extension->Filter->DeadVolumeList, &Extension->ListEntry); if (!IsFinalRemove) { IoInvalidateDeviceRelations(Extension->Filter->Pdo, BusRelations); } } else { if (NeedLock) { VspAcquire(Extension->Root); } RtlDeleteElementGenericTable(&Extension->Root->UsedDevnodeNumbers, &Extension->DevnodeNumber); if (NeedLock) { VspRelease(Extension->Root); } IoDeleteDevice(Extension->DeviceObject); } } NTSTATUS VspMarkFreeSpaceInBitmap( IN PVOLUME_EXTENSION Extension, IN HANDLE UseThisHandle, IN PRTL_BITMAP BitmapToSet ) /*++ Routine Description: This routine opens the snapshot volume and marks off the free space in the NTFS bitmap as 'ignorable'. Arguments: Extension - Supplies the volume extension. Return Value: NTSTATUS --*/ { WCHAR buffer[100]; KEVENT event; IO_STATUS_BLOCK ioStatus; UNICODE_STRING fileName; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE h; LARGE_INTEGER timeout; BOOLEAN isNtfs; FILE_FS_SIZE_INFORMATION fsSize; ULONG bitmapSize; STARTING_LCN_INPUT_BUFFER input; PVOLUME_BITMAP_BUFFER output; RTL_BITMAP freeSpaceBitmap; ULONG bpc, f, numBits, startBit, s, n, i; KIRQL irql; ULONG r; if (UseThisHandle) { h = UseThisHandle; } else { swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", Extension->VolumeNumber); RtlInitUnicodeString(&fileName, buffer); InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent(&event, NotificationEvent, FALSE); timeout.QuadPart = -10*1000; // 1 millisecond. for (i = 0; i < 5000; i++) { status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(status)) { break; } if (status != STATUS_NO_SUCH_DEVICE) { return status; } KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout); } if (!NT_SUCCESS(status)) { return status; } } status = VspIsNtfs(h, &isNtfs); if (!NT_SUCCESS(status) || !isNtfs) { if (!UseThisHandle) { ZwClose(h); } return status; } status = ZwQueryVolumeInformationFile(h, &ioStatus, &fsSize, sizeof(fsSize), FileFsSizeInformation); if (!NT_SUCCESS(status)) { if (!UseThisHandle) { ZwClose(h); } return status; } bitmapSize = (ULONG) ((fsSize.TotalAllocationUnits.QuadPart+7)/8 + FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) + 3); input.StartingLcn.QuadPart = 0; output = (PVOLUME_BITMAP_BUFFER) ExAllocatePoolWithTag(PagedPool, bitmapSize, VOLSNAP_TAG_BITMAP); if (!output) { if (!UseThisHandle) { ZwClose(h); } return STATUS_INSUFFICIENT_RESOURCES; } status = ZwFsControlFile(h, NULL, NULL, NULL, &ioStatus, FSCTL_GET_VOLUME_BITMAP, &input, sizeof(input), output, bitmapSize); if (!UseThisHandle) { ZwClose(h); } if (!NT_SUCCESS(status)) { ExFreePool(output); return status; } ASSERT(output->BitmapSize.HighPart == 0); RtlInitializeBitMap(&freeSpaceBitmap, (PULONG) output->Buffer, output->BitmapSize.LowPart); bpc = fsSize.BytesPerSector*fsSize.SectorsPerAllocationUnit; if (bpc < BLOCK_SIZE) { f = BLOCK_SIZE/bpc; } else { f = bpc/BLOCK_SIZE; } startBit = 0; for (;;) { if (startBit < freeSpaceBitmap.SizeOfBitMap) { numBits = RtlFindNextForwardRunClear(&freeSpaceBitmap, startBit, &startBit); } else { numBits = 0; } if (!numBits) { break; } if (bpc == BLOCK_SIZE) { s = startBit; n = numBits; } else if (bpc < BLOCK_SIZE) { s = (startBit + f - 1)/f; r = startBit%f; if (r) { if (numBits > f - r) { n = numBits - (f - r); } else { n = 0; } } else { n = numBits; } n /= f; } else { s = startBit*f; n = numBits*f; } if (n) { if (BitmapToSet) { RtlSetBits(BitmapToSet, s, n); } else { KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->VolumeBlockBitmap) { if (Extension->IgnorableProduct) { for (i = 0; i < n; i++) { if (RtlCheckBit(Extension->IgnorableProduct, i + s)) { RtlSetBit(Extension->VolumeBlockBitmap, i + s); } } } else { RtlSetBits(Extension->VolumeBlockBitmap, s, n); } } KeReleaseSpinLock(&Extension->SpinLock, irql); } } startBit += numBits; } ExFreePool(output); return STATUS_SUCCESS; } NTSTATUS VspCommitSnapshot( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine commits the prepared snapshot. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { KIRQL irql; PVOLUME_EXTENSION extension, previousExtension; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; InterlockedIncrement(&Filter->IgnoreCopyData); VspAcquire(Filter->Root); KeAcquireSpinLock(&Filter->SpinLock, &irql); extension = Filter->PreparedSnapshot; Filter->PreparedSnapshot = NULL; KeReleaseSpinLock(&Filter->SpinLock, irql); if (!extension) { VspRelease(Filter->Root); InterlockedDecrement(&Filter->IgnoreCopyData); return STATUS_INVALID_PARAMETER; } if (extension->DiffAreaFile->Filter->ProtectedBlocksBitmap) { VspRelease(Filter->Root); VspCleanupInitialSnapshot(extension, TRUE, FALSE); InterlockedDecrement(&Filter->IgnoreCopyData); return STATUS_INVALID_PARAMETER; } if (extension->IsPersistent && extension->Filter->ProtectedBlocksBitmap) { VspRelease(Filter->Root); VspCleanupInitialSnapshot(extension, TRUE, FALSE); InterlockedDecrement(&Filter->IgnoreCopyData); return STATUS_INVALID_PARAMETER; } if (!Filter->HoldIncomingWrites) { VspRelease(Filter->Root); VspCleanupInitialSnapshot(extension, TRUE, FALSE); InterlockedDecrement(&Filter->IgnoreCopyData); return Filter->LastReleaseDueToMemoryPressure ? STATUS_INSUFFICIENT_RESOURCES : STATUS_INVALID_PARAMETER; } extension->IgnoreCopyDataReference = TRUE; KeAcquireSpinLock(&Filter->SpinLock, &irql); InterlockedExchange(&Filter->SnapshotsPresent, TRUE); if (extension->IsPersistent) { InterlockedExchange(&Filter->PersistentSnapshots, TRUE); } else { InterlockedExchange(&Filter->PersistentSnapshots, FALSE); } InsertTailList(&Filter->VolumeList, &extension->ListEntry); InterlockedIncrement(&Filter->EpicNumber); extension->IsPreExposure = FALSE; KeReleaseSpinLock(&Filter->SpinLock, irql); l = extension->ListEntry.Blink; if (l != &Filter->VolumeList) { previousExtension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); KeAcquireSpinLock(&previousExtension->SpinLock, &irql); ExFreePool(previousExtension->VolumeBlockBitmap->Buffer); ExFreePool(previousExtension->VolumeBlockBitmap); previousExtension->VolumeBlockBitmap = NULL; KeReleaseSpinLock(&previousExtension->SpinLock, irql); extension->SnapshotOrderNumber = previousExtension->SnapshotOrderNumber + 1; } else { extension->SnapshotOrderNumber = 1; } KeQuerySystemTime(&extension->CommitTimeStamp); if (Filter->UsedForCrashdump && extension->IsPersistent) { extension->ContainsCrashdumpFile = TRUE; } VspRelease(Filter->Root); return STATUS_SUCCESS; } VOID VspCheckMaxSizeWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; ULONG i, n; PLIST_ENTRY l; KIRQL irql; LIST_ENTRY closeList, deleteList; PVOLUME_EXTENSION extension; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); VspFreeContext(filter->Root, context); KeWaitForSingleObject(&filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); VspAcquire(filter->Root); if (filter->IsRemoved) { VspRelease(filter->Root); ObDereferenceObject(filter->DeviceObject); return; } i = 0; for (l = filter->VolumeList.Flink; l != &filter->VolumeList; l = l->Flink) { i++; } if (i > VSP_MAX_SNAPSHOTS) { n = i - VSP_MAX_SNAPSHOTS; } else { n = 0; } InitializeListHead(&closeList); InitializeListHead(&deleteList); for (i = 0; i < n; i++) { extension = CONTAINING_RECORD(filter->VolumeList.Flink, VOLUME_EXTENSION, ListEntry); VspLogError(extension, NULL, VS_DELETE_TO_MAX_NUMBER, STATUS_SUCCESS, 0, TRUE); VspDeleteOldestSnapshot(filter, &closeList, &deleteList, FALSE, FALSE); } KeAcquireSpinLock(&filter->SpinLock, &irql); if (!filter->MaximumVolumeSpace) { KeReleaseSpinLock(&filter->SpinLock, irql); VspRelease(filter->Root); VspCloseDiffAreaFiles(&closeList, &deleteList); ObDereferenceObject(filter->DeviceObject); return; } while (filter->AllocatedVolumeSpace > filter->MaximumVolumeSpace) { KeReleaseSpinLock(&filter->SpinLock, irql); extension = CONTAINING_RECORD(filter->VolumeList.Flink, VOLUME_EXTENSION, ListEntry); VspLogError(extension, NULL, VS_DELETE_TO_TRIM_SPACE, STATUS_SUCCESS, 2, TRUE); VspDeleteOldestSnapshot(filter, &closeList, &deleteList, FALSE, FALSE); KeAcquireSpinLock(&filter->SpinLock, &irql); if (!filter->MaximumVolumeSpace) { break; } } KeReleaseSpinLock(&filter->SpinLock, irql); VspRelease(filter->Root); VspCloseDiffAreaFiles(&closeList, &deleteList); ObDereferenceObject(filter->DeviceObject); } VOID VspCheckMaxSize( IN PFILTER_EXTENSION Filter ) { PVSP_CONTEXT context; context = VspAllocateContext(Filter->Root); if (!context) { return; } ObReferenceObject(Filter->DeviceObject); context->Type = VSP_CONTEXT_TYPE_FILTER; context->Filter.Filter = Filter; ExInitializeWorkItem(&context->WorkItem, VspCheckMaxSizeWorker, context); VspQueueLowPriorityWorkItem(Filter->Root, &context->WorkItem); } NTSTATUS VspEndCommitSnapshot( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine commits the prepared snapshot. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PLIST_ENTRY l; PVOLUME_EXTENSION extension; WCHAR buffer[100]; UNICODE_STRING volumeName; PVOLSNAP_NAME output; NTSTATUS status; LARGE_INTEGER timeout; PVSP_CONTEXT context; VspAcquire(Filter->Root); l = Filter->VolumeList.Blink; if (l == &Filter->VolumeList) { VspRelease(Filter->Root); return STATUS_INVALID_PARAMETER; } extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); if (extension->HasEndCommit) { VspRelease(Filter->Root); return STATUS_INVALID_PARAMETER; } if (irpSp->Parameters.DeviceIoControl.InputBufferLength) { status = VspSetApplicationInfo(extension, Irp); if (!NT_SUCCESS(status)) { if (extension->IgnoreCopyDataReference) { extension->IgnoreCopyDataReference = FALSE; InterlockedDecrement(&Filter->IgnoreCopyData); } VspRelease(Filter->Root); return status; } } swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", extension->VolumeNumber); RtlInitUnicodeString(&volumeName, buffer); Irp->IoStatus.Information = FIELD_OFFSET(VOLSNAP_NAME, Name) + volumeName.Length + sizeof(WCHAR); if (Irp->IoStatus.Information > irpSp->Parameters.DeviceIoControl.OutputBufferLength) { if (extension->IgnoreCopyDataReference) { extension->IgnoreCopyDataReference = FALSE; InterlockedDecrement(&Filter->IgnoreCopyData); } VspRelease(Filter->Root); Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } output = (PVOLSNAP_NAME) Irp->AssociatedIrp.SystemBuffer; output->NameLength = volumeName.Length; RtlCopyMemory(output->Name, volumeName.Buffer, output->NameLength + sizeof(WCHAR)); extension->HasEndCommit = TRUE; if (extension->IsPersistent) { VspAcquireNonPagedResource(extension, NULL, FALSE); status = VspCheckOnDiskNotCommitted(extension); VspReleaseNonPagedResource(extension); } else { status = STATUS_SUCCESS; } if (NT_SUCCESS(status)) { VspTruncatePreviousDiffArea(extension); } if (!KeCancelTimer(&Filter->EndCommitTimer)) { ObReferenceObject(Filter->DeviceObject); } KeResetEvent(&Filter->EndCommitProcessCompleted); if (extension->IsInstalled) { context = VspAllocateContext(Filter->Root); if (!context) { InterlockedExchange(&Filter->HibernatePending, FALSE); KeSetEvent(&Filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); VspRelease(Filter->Root); ObDereferenceObject(Filter->DeviceObject); return STATUS_INSUFFICIENT_RESOURCES; } ObReferenceObject(extension->DeviceObject); ObReferenceObject(Filter->TargetObject); VspRelease(Filter->Root); context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = extension; context->Extension.Irp = NULL; ExInitializeWorkItem(&context->WorkItem, VspSetIgnorableBlocksInBitmapWorker, context); VspQueueWorkItem(Filter->Root, &context->WorkItem, 0); } else { timeout.QuadPart = (LONGLONG) -10*1000*1000*120*10; // 20 minutes. KeSetTimer(&Filter->EndCommitTimer, timeout, &Filter->EndCommitTimerDpc); VspRelease(Filter->Root); IoInvalidateDeviceRelations(Filter->Pdo, BusRelations); } VspCheckMaxSize(Filter); return STATUS_SUCCESS; } NTSTATUS VspVolumeRefCountCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Extension ) { PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) Extension; VspDecrementVolumeRefCount(extension); return STATUS_SUCCESS; } NTSTATUS VspQueryNamesOfSnapshots( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine returns the names of all of the snapshots for this filter. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_NAMES output; PLIST_ENTRY l; PVOLUME_EXTENSION extension; WCHAR buffer[100]; UNICODE_STRING name; PWCHAR buf; Irp->IoStatus.Information = FIELD_OFFSET(VOLSNAP_NAMES, Names); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } KeWaitForSingleObject(&Filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); output = (PVOLSNAP_NAMES) Irp->AssociatedIrp.SystemBuffer; VspAcquire(Filter->Root); output->MultiSzLength = sizeof(WCHAR); for (l = Filter->VolumeList.Flink; l != &Filter->VolumeList; l = l->Flink) { extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", extension->VolumeNumber); RtlInitUnicodeString(&name, buffer); output->MultiSzLength += name.Length + sizeof(WCHAR); } if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information + output->MultiSzLength) { VspRelease(Filter->Root); return STATUS_BUFFER_OVERFLOW; } Irp->IoStatus.Information += output->MultiSzLength; buf = output->Names; for (l = Filter->VolumeList.Flink; l != &Filter->VolumeList; l = l->Flink) { extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); swprintf(buf, L"\\Device\\HarddiskVolumeShadowCopy%d", extension->VolumeNumber); RtlInitUnicodeString(&name, buf); buf += name.Length/sizeof(WCHAR) + 1; } *buf = 0; VspRelease(Filter->Root); return STATUS_SUCCESS; } NTSTATUS VspClearDiffArea( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine clears the list of diff areas used by this filter. This call will fail if there are any snapshots in flight. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { VspAcquire(Filter->Root); Filter->DiffAreaVolume = NULL; VspRelease(Filter->Root); return STATUS_SUCCESS; } NTSTATUS VspAddVolumeToDiffArea( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine adds the given volume to the diff area for this volume. All snapshots get a new diff area file. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_NAME input; UNICODE_STRING volumeName; PFILTER_EXTENSION filter; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLSNAP_NAME)) { VspLogError(NULL, Filter, VS_FAILURE_ADDING_DIFF_AREA, STATUS_INVALID_PARAMETER, 3, FALSE); return STATUS_INVALID_PARAMETER; } input = (PVOLSNAP_NAME) Irp->AssociatedIrp.SystemBuffer; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < (ULONG) FIELD_OFFSET(VOLSNAP_NAME, Name) + input->NameLength) { VspLogError(NULL, Filter, VS_FAILURE_ADDING_DIFF_AREA, STATUS_INVALID_PARAMETER, 4, FALSE); return STATUS_INVALID_PARAMETER; } volumeName.Length = volumeName.MaximumLength = input->NameLength; volumeName.Buffer = input->Name; VspAcquire(Filter->Root); filter = VspFindFilter(Filter->Root, Filter, &volumeName, NULL); if (!filter || (filter->TargetObject->Characteristics&FILE_REMOVABLE_MEDIA)) { VspRelease(Filter->Root); return STATUS_INVALID_PARAMETER; } if (Filter->DiffAreaVolume) { VspRelease(Filter->Root); VspLogError(NULL, Filter, VS_FAILURE_ADDING_DIFF_AREA, STATUS_INVALID_PARAMETER, 5, FALSE); return STATUS_INVALID_PARAMETER; } Filter->DiffAreaVolume = filter; VspRelease(Filter->Root); return STATUS_SUCCESS; } NTSTATUS VspQueryDiffArea( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp ) /*++ Routine Description: This routine the list of volumes that make up the diff area for this volume. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION Filter = (PFILTER_EXTENSION) DeviceExtension; PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_NAMES output; PLIST_ENTRY l; PFILTER_EXTENSION filter; KEVENT event; PMOUNTDEV_NAME name; CHAR buffer[512]; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; PWCHAR buf; Irp->IoStatus.Information = FIELD_OFFSET(VOLSNAP_NAMES, Names); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } output = (PVOLSNAP_NAMES) Irp->AssociatedIrp.SystemBuffer; VspAcquire(Filter->Root); output->MultiSzLength = sizeof(WCHAR); if (Filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { filter = Filter->DiffAreaVolume; } else { ASSERT(extension->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); if (extension->DiffAreaFile) { filter = extension->DiffAreaFile->Filter; } else { filter = NULL; } } if (filter) { KeInitializeEvent(&event, NotificationEvent, FALSE); name = (PMOUNTDEV_NAME) buffer; irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, filter->TargetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { VspRelease(Filter->Root); Irp->IoStatus.Information = 0; return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { VspRelease(Filter->Root); Irp->IoStatus.Information = 0; return status; } output->MultiSzLength += name->NameLength + sizeof(WCHAR); } if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information + output->MultiSzLength) { VspRelease(Filter->Root); return STATUS_BUFFER_OVERFLOW; } Irp->IoStatus.Information += output->MultiSzLength; buf = output->Names; if (Filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { filter = Filter->DiffAreaVolume; } else { ASSERT(extension->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); if (extension->DiffAreaFile) { filter = extension->DiffAreaFile->Filter; } else { filter = NULL; } } if (filter) { KeInitializeEvent(&event, NotificationEvent, FALSE); name = (PMOUNTDEV_NAME) buffer; irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, filter->TargetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { VspRelease(Filter->Root); Irp->IoStatus.Information = 0; return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { VspRelease(Filter->Root); Irp->IoStatus.Information = 0; return status; } RtlCopyMemory(buf, name->Name, name->NameLength); buf += name->NameLength/sizeof(WCHAR); *buf++ = 0; } *buf = 0; VspRelease(Filter->Root); return STATUS_SUCCESS; } NTSTATUS VspQueryDiffAreaSizes( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp ) /*++ Routine Description: This routine returns the diff area sizes for this volume. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceExtension; PVOLUME_EXTENSION extension = (PVOLUME_EXTENSION) DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_DIFF_AREA_SIZES output = (PVOLSNAP_DIFF_AREA_SIZES) Irp->AssociatedIrp.SystemBuffer; KIRQL irql; Irp->IoStatus.Information = sizeof(VOLSNAP_DIFF_AREA_SIZES); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { KeAcquireSpinLock(&filter->SpinLock, &irql); output->UsedVolumeSpace = filter->UsedVolumeSpace; output->AllocatedVolumeSpace = filter->AllocatedVolumeSpace; output->MaximumVolumeSpace = filter->MaximumVolumeSpace; KeReleaseSpinLock(&filter->SpinLock, irql); return STATUS_SUCCESS; } ASSERT(extension->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); RtlZeroMemory(output, sizeof(VOLSNAP_DIFF_AREA_SIZES)); VspAcquireNonPagedResource(extension, NULL, FALSE); if (extension->DiffAreaFile) { output->UsedVolumeSpace = extension->DiffAreaFile->NextAvailable; output->AllocatedVolumeSpace = extension->DiffAreaFile->AllocatedFileSize; } VspReleaseNonPagedResource(extension); return STATUS_SUCCESS; } NTSTATUS VspQueryOriginalVolumeName( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) /*++ Routine Description: This routine returns the original volume name for the given volume snapshot. Arguments: Extension - Supplies the volume extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = Extension->Filter; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_NAME output = (PVOLSNAP_NAME) Irp->AssociatedIrp.SystemBuffer; PMOUNTDEV_NAME name; CHAR buffer[512]; KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; Irp->IoStatus.Information = FIELD_OFFSET(VOLSNAP_NAME, Name); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } name = (PMOUNTDEV_NAME) buffer; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, filter->TargetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { Irp->IoStatus.Information = 0; return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { Irp->IoStatus.Information = 0; return status; } output->NameLength = name->NameLength; if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information + output->NameLength) { return STATUS_BUFFER_OVERFLOW; } RtlCopyMemory(output->Name, name->Name, output->NameLength); Irp->IoStatus.Information += output->NameLength; return STATUS_SUCCESS; } NTSTATUS VspQueryConfigInfo( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) /*++ Routine Description: This routine returns the configuration information for this volume snapshot. Arguments: Extension - Supplies the volume extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_CONFIG_INFO output = (PVOLSNAP_CONFIG_INFO) Irp->AssociatedIrp.SystemBuffer; Irp->IoStatus.Information = sizeof(ULONG); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } output->Attributes = 0; if (Extension->IsPersistent) { output->Attributes |= VOLSNAP_ATTRIBUTE_PERSISTENT; } if (irpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(VOLSNAP_CONFIG_INFO)) { Irp->IoStatus.Information = sizeof(VOLSNAP_CONFIG_INFO); output->Reserved = 0; output->SnapshotCreationTime = Extension->CommitTimeStamp; } return STATUS_SUCCESS; } NTSTATUS VspWriteApplicationInfo( IN PVOLUME_EXTENSION Extension ) { LONGLONG targetOffset, fileOffset; NTSTATUS status; PVSP_BLOCK_APP_INFO appInfoBlock; VspAcquireNonPagedResource(Extension, NULL, FALSE); targetOffset = Extension->DiffAreaFile->ApplicationInfoTargetOffset; ASSERT(targetOffset); status = VspSynchronousIo(Extension->Filter->SnapshotOnDiskIrp, Extension->DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, targetOffset, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Extension); return status; } appInfoBlock = (PVSP_BLOCK_APP_INFO) MmGetMdlVirtualAddress(Extension->Filter->SnapshotOnDiskIrp-> MdlAddress); if (appInfoBlock->Header.Signature != VSP_DIFF_AREA_FILE_GUID || appInfoBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || appInfoBlock->Header.BlockType != VSP_BLOCK_TYPE_APP_INFO || appInfoBlock->Header.ThisVolumeOffset != targetOffset || appInfoBlock->Header.NextVolumeOffset) { VspReleaseNonPagedResource(Extension); return STATUS_INVALID_PARAMETER; } appInfoBlock->AppInfoSize = Extension->ApplicationInformationSize; RtlCopyMemory((PCHAR) appInfoBlock + VSP_OFFSET_TO_APP_INFO, Extension->ApplicationInformation, Extension->ApplicationInformationSize); status = VspSynchronousIo(Extension->Filter->SnapshotOnDiskIrp, Extension->DiffAreaFile->Filter->TargetObject, IRP_MJ_WRITE, targetOffset, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Extension); return status; } VspReleaseNonPagedResource(Extension); return STATUS_SUCCESS; } NTSTATUS VspSetApplicationInfo( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) /*++ Routine Description: This routine sets the application info. Arguments: Extension - Supplies the volume extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_APPLICATION_INFO input = (PVOLSNAP_APPLICATION_INFO) Irp->AssociatedIrp.SystemBuffer; PVOID newAppInfo, oldAppInfo; NTSTATUS status; Irp->IoStatus.Information = 0; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLSNAP_APPLICATION_INFO)) { return STATUS_INVALID_PARAMETER; } if (input->InformationLength > VSP_MAX_APP_INFO_SIZE) { return STATUS_INVALID_PARAMETER; } if (input->InformationLength < sizeof(GUID)) { return STATUS_INVALID_PARAMETER; } if (irpSp->Parameters.DeviceIoControl.InputBufferLength < (LONGLONG) FIELD_OFFSET(VOLSNAP_APPLICATION_INFO, Information) + input->InformationLength) { return STATUS_INVALID_PARAMETER; } newAppInfo = ExAllocatePoolWithQuotaTag((POOL_TYPE) (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE), input->InformationLength, VOLSNAP_TAG_APP_INFO); if (!newAppInfo) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(newAppInfo, input->Information, input->InformationLength); KeEnterCriticalRegion(); VspAcquirePagedResource(Extension, NULL); Extension->ApplicationInformationSize = input->InformationLength; oldAppInfo = Extension->ApplicationInformation; Extension->ApplicationInformation = newAppInfo; VspReleasePagedResource(Extension); KeLeaveCriticalRegion(); InterlockedIncrement(&Extension->Filter->EpicNumber); if (oldAppInfo) { ExFreePool(oldAppInfo); } if (Extension->IsPersistent) { status = VspWriteApplicationInfo(Extension); if (!NT_SUCCESS(status)) { return status; } } return STATUS_SUCCESS; } NTSTATUS VspQueryApplicationInfo( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) /*++ Routine Description: This routine queries the application info. Arguments: Extension - Supplies the volume extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_APPLICATION_INFO output = (PVOLSNAP_APPLICATION_INFO) Irp->AssociatedIrp.SystemBuffer; PVOID appInfo; Irp->IoStatus.Information = FIELD_OFFSET(VOLSNAP_APPLICATION_INFO, Information); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } KeEnterCriticalRegion(); VspAcquirePagedResource(Extension, NULL); output->InformationLength = Extension->ApplicationInformationSize; if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information + output->InformationLength) { VspReleasePagedResource(Extension); KeLeaveCriticalRegion(); return STATUS_BUFFER_OVERFLOW; } Irp->IoStatus.Information += output->InformationLength; RtlCopyMemory(output->Information, Extension->ApplicationInformation, output->InformationLength); VspReleasePagedResource(Extension); KeLeaveCriticalRegion(); return STATUS_SUCCESS; } NTSTATUS VspCheckSecurity( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { SECURITY_SUBJECT_CONTEXT securityContext; BOOLEAN accessGranted; NTSTATUS status; ACCESS_MASK grantedAccess; SeCaptureSubjectContext(&securityContext); SeLockSubjectContext(&securityContext); accessGranted = FALSE; status = STATUS_ACCESS_DENIED; _try { accessGranted = SeAccessCheck( Filter->Pdo->SecurityDescriptor, &securityContext, TRUE, FILE_READ_DATA, 0, NULL, IoGetFileObjectGenericMapping(), Irp->RequestorMode, &grantedAccess, &status); } _finally { SeUnlockSubjectContext(&securityContext); SeReleaseSubjectContext(&securityContext); } if (!accessGranted) { return status; } return STATUS_SUCCESS; } NTSTATUS VspAutoCleanup( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) /*++ Routine Description: This routine remembers the given File Object so that when it is cleaned up, all snapshots will be cleaned up with it. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; KIRQL irql; IoAcquireCancelSpinLock(&irql); if (Filter->AutoCleanupFileObject) { IoReleaseCancelSpinLock(irql); return STATUS_INVALID_PARAMETER; } Filter->AutoCleanupFileObject = irpSp->FileObject; IoReleaseCancelSpinLock(irql); return STATUS_SUCCESS; } VOID VspDeleteSnapshotWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PIRP irp = context->Dispatch.Irp; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp); PVOLUME_EXTENSION oldestExtension; PVOLSNAP_NAME name; WCHAR buffer[100]; UNICODE_STRING name1, name2; LIST_ENTRY listOfDiffAreaFileToClose; LIST_ENTRY listOfDeviceObjectsToDelete; NTSTATUS status; ASSERT(context->Type == VSP_CONTEXT_TYPE_DISPATCH); KeWaitForSingleObject(&filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); InitializeListHead(&listOfDiffAreaFileToClose); InitializeListHead(&listOfDeviceObjectsToDelete); VspAcquire(filter->Root); if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_VOLSNAP_DELETE_SNAPSHOT) { if (IsListEmpty(&filter->VolumeList)) { status = STATUS_INVALID_PARAMETER; } else { oldestExtension = CONTAINING_RECORD(filter->VolumeList.Flink, VOLUME_EXTENSION, ListEntry); swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", oldestExtension->VolumeNumber); RtlInitUnicodeString(&name1, buffer); name = (PVOLSNAP_NAME) irp->AssociatedIrp.SystemBuffer; name2.Length = name2.MaximumLength = name->NameLength; name2.Buffer = name->Name; if (RtlEqualUnicodeString(&name1, &name2, TRUE)) { status = STATUS_SUCCESS; } else { status = STATUS_NOT_SUPPORTED; } } } else { status = STATUS_SUCCESS; } if (NT_SUCCESS(status)) { status = VspDeleteOldestSnapshot(filter, &listOfDiffAreaFileToClose, &listOfDeviceObjectsToDelete, FALSE, FALSE); } VspRelease(filter->Root); VspCloseDiffAreaFiles(&listOfDiffAreaFileToClose, &listOfDeviceObjectsToDelete); IoFreeWorkItem(context->Dispatch.IoWorkItem); VspFreeContext(filter->Root, context); irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } NTSTATUS VspDeleteSnapshotPost( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVSP_CONTEXT context; PVOLSNAP_NAME name; Irp->IoStatus.Information = 0; if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_VOLSNAP_DELETE_SNAPSHOT) { if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLSNAP_NAME)) { return STATUS_INVALID_PARAMETER; } name = (PVOLSNAP_NAME) Irp->AssociatedIrp.SystemBuffer; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < (ULONG) FIELD_OFFSET(VOLSNAP_NAME, Name) + name->NameLength) { return STATUS_INVALID_PARAMETER; } } context = VspAllocateContext(Filter->Root); if (!context) { return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_DISPATCH; context->Dispatch.IoWorkItem = IoAllocateWorkItem(Filter->DeviceObject); if (!context->Dispatch.IoWorkItem) { VspFreeContext(Filter->Root, context); return STATUS_INSUFFICIENT_RESOURCES; } IoMarkIrpPending(Irp); context->Dispatch.Irp = Irp; IoQueueWorkItem(context->Dispatch.IoWorkItem, VspDeleteSnapshotWorker, DelayedWorkQueue, context); return STATUS_PENDING; } VOID VspCheckCodeLocked( IN PDO_EXTENSION RootExtension, IN BOOLEAN CallerHoldingSemaphore ) { if (RootExtension->IsCodeLocked) { return; } if (!CallerHoldingSemaphore) { VspAcquire(RootExtension); } if (RootExtension->IsCodeLocked) { if (!CallerHoldingSemaphore) { VspRelease(RootExtension); } return; } MmLockPagableCodeSection(VspCheckCodeLocked); InterlockedExchange(&RootExtension->IsCodeLocked, TRUE); if (!CallerHoldingSemaphore) { VspRelease(RootExtension); } } NTSTATUS VspSetMaxDiffAreaSize( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_DIFF_AREA_SIZES input = (PVOLSNAP_DIFF_AREA_SIZES) Irp->AssociatedIrp.SystemBuffer; NTSTATUS status; KIRQL irql; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLSNAP_DIFF_AREA_SIZES)) { return STATUS_INVALID_PARAMETER; } if (input->MaximumVolumeSpace < 0) { return STATUS_INVALID_PARAMETER; } VspAcquireNonPagedResource(Filter, NULL, FALSE); status = VspCreateStartBlock(Filter, Filter->FirstControlBlockVolumeOffset, input->MaximumVolumeSpace); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); return status; } KeAcquireSpinLock(&Filter->SpinLock, &irql); Filter->MaximumVolumeSpace = input->MaximumVolumeSpace; InterlockedIncrement(&Filter->EpicNumber); KeReleaseSpinLock(&Filter->SpinLock, irql); VspReleaseNonPagedResource(Filter); VspCheckMaxSize(Filter); return STATUS_SUCCESS; } BOOLEAN VspIsNtfsBootSector( IN PFILTER_EXTENSION Filter, IN PVOID BootSector ) { PUCHAR p = (PUCHAR) BootSector; LONGLONG numSectors; if (p[3] != 'N' || p[4] != 'T' || p[5] != 'F' || p[6] != 'S' && p[510] != 0x55 || p[511] != 0xAA) { return FALSE; } numSectors = *((PLONGLONG) (p + 0x28)); if (numSectors > VspQueryVolumeSize(Filter)) { return FALSE; } return TRUE; } NTSTATUS VspReadApplicationInfo( IN PVSP_DIFF_AREA_FILE DiffAreaFile ) { PVOLUME_EXTENSION extension = DiffAreaFile->Extension; LONGLONG targetOffset; NTSTATUS status; PVSP_BLOCK_APP_INFO appInfoBlock; targetOffset = DiffAreaFile->ApplicationInfoTargetOffset; if (!targetOffset) { return STATUS_SUCCESS; } status = VspSynchronousIo(DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, targetOffset, 0); if (!NT_SUCCESS(status)) { return status; } appInfoBlock = (PVSP_BLOCK_APP_INFO) MmGetMdlVirtualAddress(DiffAreaFile->TableUpdateIrp->MdlAddress); if (!IsEqualGUID(appInfoBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || appInfoBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || appInfoBlock->Header.BlockType != VSP_BLOCK_TYPE_APP_INFO || appInfoBlock->Header.ThisVolumeOffset != targetOffset || appInfoBlock->Header.NextVolumeOffset || appInfoBlock->AppInfoSize > VSP_MAX_APP_INFO_SIZE) { return STATUS_INVALID_PARAMETER; } ASSERT(!extension->ApplicationInformation); if (!appInfoBlock->AppInfoSize) { return STATUS_SUCCESS; } extension->ApplicationInformation = ExAllocatePoolWithTag(NonPagedPool, appInfoBlock->AppInfoSize, VOLSNAP_TAG_APP_INFO); if (!extension->ApplicationInformation) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(extension->ApplicationInformation, (PCHAR) appInfoBlock + VSP_OFFSET_TO_APP_INFO, appInfoBlock->AppInfoSize); extension->ApplicationInformationSize = appInfoBlock->AppInfoSize; return STATUS_SUCCESS; } NTSTATUS VspCreateSnapshotExtension( IN PFILTER_EXTENSION Filter, IN PVSP_LOOKUP_TABLE_ENTRY LookupEntry, IN BOOLEAN IsNewestSnapshot ) { ULONG volumeNumber, i; WCHAR buffer[100]; UNICODE_STRING volumeName; NTSTATUS status, status2; PDEVICE_OBJECT deviceObject; PVOLUME_EXTENSION extension; PVSP_DIFF_AREA_FILE diffAreaFile; PVOID buf; PMDL mdl; KIRQL irql; PSECURITY_DESCRIPTOR sd; BOOLEAN ma; if (IsNewestSnapshot && (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_CRASHDUMP)) { VspLogError(NULL, Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, STATUS_SUCCESS, 5, FALSE); return STATUS_NO_SUCH_DEVICE; } if (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_DETECTION) { VspLogError(NULL, Filter, VS_ABORT_DIRTY_DETECTION, STATUS_SUCCESS, 0, FALSE); return STATUS_NO_SUCH_DEVICE; } volumeNumber = (ULONG) InterlockedIncrement(&Filter->Root->NextVolumeNumber); swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d", volumeNumber); RtlInitUnicodeString(&volumeName, buffer); status = IoCreateDevice(Filter->Root->DriverObject, sizeof(VOLUME_EXTENSION), &volumeName, FILE_DEVICE_VIRTUAL_DISK, 0, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { return status; } extension = (PVOLUME_EXTENSION) deviceObject->DeviceExtension; RtlZeroMemory(extension, sizeof(VOLUME_EXTENSION)); extension->DeviceObject = deviceObject; extension->Root = Filter->Root; extension->DeviceExtensionType = DEVICE_EXTENSION_VOLUME; KeInitializeSpinLock(&extension->SpinLock); extension->Filter = Filter; if (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_OFFLINE) { extension->IsOffline = TRUE; } extension->RefCount = 1; InitializeListHead(&extension->WriteContextList); KeInitializeEvent(&extension->ZeroRefEvent, NotificationEvent, FALSE); extension->HasEndCommit = TRUE; extension->IsPersistent = TRUE; extension->IsDetected = TRUE; extension->RootSemaphoreHeld = TRUE; if (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_VISIBLE) { extension->IsVisible = TRUE; } if (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_NO_DIFF_AREA_FILL) { extension->NoDiffAreaFill = TRUE; } extension->CommitTimeStamp = LookupEntry->SnapshotTime; extension->VolumeNumber = volumeNumber; extension->SnapshotGuid = LookupEntry->SnapshotGuid; extension->SnapshotOrderNumber = LookupEntry->SnapshotOrderNumber; RtlInitializeGenericTable(&extension->VolumeBlockTable, VspTableCompareRoutine, VspTableAllocateRoutine, VspTableFreeRoutine, extension); RtlInitializeGenericTable(&extension->TempVolumeBlockTable, VspTableCompareRoutine, VspTempTableAllocateRoutine, VspTempTableFreeRoutine, extension); RtlInitializeGenericTable(&extension->CopyBackPointerTable, VspTableCompareRoutine, VspTableAllocateRoutine, VspTableFreeRoutine, extension); extension->DiffAreaFileIncrease = NOMINAL_DIFF_AREA_FILE_GROWTH; diffAreaFile = (PVSP_DIFF_AREA_FILE) ExAllocatePoolWithTag(NonPagedPool, sizeof(VSP_DIFF_AREA_FILE), VOLSNAP_TAG_DIFF_FILE); if (!diffAreaFile) { IoDeleteDevice(deviceObject); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(diffAreaFile, sizeof(VSP_DIFF_AREA_FILE)); diffAreaFile->Extension = extension; diffAreaFile->Filter = LookupEntry->DiffAreaFilter; diffAreaFile->FileHandle = LookupEntry->DiffAreaHandle; LookupEntry->DiffAreaHandle = NULL; InitializeListHead(&diffAreaFile->UnusedAllocationList); diffAreaFile->TableUpdateIrp = IoAllocateIrp((CCHAR) extension->Root->StackSize, FALSE); if (!diffAreaFile->TableUpdateIrp) { ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return STATUS_INSUFFICIENT_RESOURCES; } buf = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buf) { IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return STATUS_INSUFFICIENT_RESOURCES; } mdl = IoAllocateMdl(buf, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(mdl); diffAreaFile->TableUpdateIrp->MdlAddress = mdl; diffAreaFile->NextFreeTableEntryOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; diffAreaFile->ApplicationInfoTargetOffset = LookupEntry->ApplicationInfoStartingVolumeOffset; diffAreaFile->DiffAreaLocationDescriptionTargetOffset = LookupEntry->DiffAreaLocationDescriptionVolumeOffset; diffAreaFile->InitialBitmapVolumeOffset = LookupEntry->InitialBitmapVolumeOffset; diffAreaFile->FirstTableTargetOffset = LookupEntry->DiffAreaStartingVolumeOffset; diffAreaFile->TableTargetOffset = LookupEntry->DiffAreaStartingVolumeOffset; InitializeListHead(&diffAreaFile->TableUpdateQueue); InitializeListHead(&diffAreaFile->TableUpdatesInProgress); extension->DiffAreaFile = diffAreaFile; KeAcquireSpinLock(&diffAreaFile->Filter->SpinLock, &irql); InsertTailList(&diffAreaFile->Filter->DiffAreaFilesOnThisFilter, &diffAreaFile->FilterListEntry); diffAreaFile->FilterListEntryBeingUsed = TRUE; KeReleaseSpinLock(&diffAreaFile->Filter->SpinLock, irql); InitializeListHead(&extension->OldHeaps); extension->VolumeSize = LookupEntry->VolumeSnapshotSize; InitializeListHead(&extension->EmergencyCopyIrpQueue); InitializeListHead(&extension->WaitingForPageFileSpace); InitializeListHead(&extension->WaitingForDiffAreaSpace); status = VspCreateWorkerThread(Filter->Root); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 1, FALSE); RemoveEntryList(&diffAreaFile->FilterListEntry); IoFreeMdl(mdl); ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return status; } status = VspCreateInitialHeap(extension, TRUE); if (!NT_SUCCESS(status)) { VspDeleteWorkerThread(Filter->Root); RemoveEntryList(&diffAreaFile->FilterListEntry); IoFreeMdl(mdl); ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return status; } status = VspReadApplicationInfo(diffAreaFile); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 2, FALSE); status2 = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status2)); status2 = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status2)); VspDeleteWorkerThread(Filter->Root); RemoveEntryList(&diffAreaFile->FilterListEntry); IoFreeMdl(mdl); ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return status; } status = VspReadDiffAreaTable(diffAreaFile); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 3, FALSE); if (extension->ApplicationInformation) { ExFreePool(extension->ApplicationInformation); } status2 = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status2)); status2 = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status2)); VspDeleteWorkerThread(Filter->Root); RemoveEntryList(&diffAreaFile->FilterListEntry); IoFreeMdl(mdl); ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); IoDeleteDevice(deviceObject); return status; } status = ObGetObjectSecurity(Filter->Pdo, &sd, &ma); if (NT_SUCCESS(status)) { status = ObSetSecurityObjectByPointer(deviceObject, DACL_SECURITY_INFORMATION, sd); ObReleaseObjectSecurity(sd, ma); } if (NT_SUCCESS(status)) { extension->DevnodeNumber = VspClaimNextDevnodeNumber(extension->Root); if (!extension->DevnodeNumber) { status = STATUS_INSUFFICIENT_RESOURCES; } } if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 9, FALSE); if (extension->ApplicationInformation) { ExFreePool(extension->ApplicationInformation); } status2 = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->DiffAreaFileMap); ASSERT(NT_SUCCESS(status2)); status2 = ZwUnmapViewOfSection(extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status2)); VspDeleteWorkerThread(Filter->Root); RemoveEntryList(&diffAreaFile->FilterListEntry); IoFreeMdl(mdl); ExFreePool(buf); IoFreeIrp(diffAreaFile->TableUpdateIrp); ExFreePool(diffAreaFile); RtlDeleteElementGenericTable(&extension->Root->UsedDevnodeNumbers, &extension->DevnodeNumber); IoDeleteDevice(deviceObject); return status; } if (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_HIBERFIL_COPIED) { InterlockedExchange(&extension->HiberFileCopied, TRUE); } if (LookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_PAGEFILE_COPIED) { InterlockedExchange(&extension->PageFileCopied, TRUE); } extension->DeviceObject->AlignmentRequirement = extension->Filter->Pdo->AlignmentRequirement | extension->DiffAreaFile->Filter->Pdo->AlignmentRequirement; InsertTailList(&Filter->VolumeList, &extension->ListEntry); deviceObject->Flags |= DO_DIRECT_IO; deviceObject->StackSize = (CCHAR) Filter->Root->StackSize + 1; deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; extension->RootSemaphoreHeld = FALSE; return STATUS_SUCCESS; } BOOLEAN VspAreSnapshotsComplete( IN PFILTER_EXTENSION Filter ) { LONGLONG previousOrderNumber; PLIST_ENTRY l; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; VspAcquireNonPagedResource(Filter, NULL, FALSE); previousOrderNumber = 0; for (l = Filter->SnapshotLookupTableEntries.Flink; l != &Filter->SnapshotLookupTableEntries; l = l->Flink) { lookupEntry = CONTAINING_RECORD(l, VSP_LOOKUP_TABLE_ENTRY, SnapshotFilterListEntry); if (!lookupEntry->DiffAreaFilter) { VspReleaseNonPagedResource(Filter); return FALSE; } if (previousOrderNumber && previousOrderNumber + 1 != lookupEntry->SnapshotOrderNumber) { VspReleaseNonPagedResource(Filter); ASSERT(FALSE); return FALSE; } previousOrderNumber = lookupEntry->SnapshotOrderNumber; ASSERT(previousOrderNumber); } VspReleaseNonPagedResource(Filter); return TRUE; } VOID VspSignalWorker( IN PVOID Event ) { KeSetEvent((PKEVENT) Event, IO_NO_INCREMENT, FALSE); } PVOID VspInsertWithAllocateAndWait( IN PVOLUME_EXTENSION Extension, IN PRTL_GENERIC_TABLE Table, IN PVOID TableEntry ) { NTSTATUS status = STATUS_SUCCESS; PTRANSLATION_TABLE_ENTRY t1, t2; PVOID r; PVOID nodeOrParent; TABLE_SEARCH_RESULT searchResult; KEVENT event; KIRQL irql; _try { t1 = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTableFull(Table, TableEntry, &nodeOrParent, &searchResult); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) { return NULL; } if (t1) { t2 = (PTRANSLATION_TABLE_ENTRY) TableEntry; t1->TargetObject = t2->TargetObject; t1->Flags = t2->Flags; t1->TargetOffset = t2->TargetOffset; return t1; } _try { r = RtlInsertElementGenericTableFull(Table, TableEntry, sizeof(TRANSLATION_TABLE_ENTRY), NULL, nodeOrParent, searchResult); } _except (EXCEPTION_EXECUTE_HANDLER) { r = NULL; } if (!r) { KeInitializeEvent(&event, NotificationEvent, FALSE); KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->PageFileSpaceCreatePending) { ExInitializeWorkItem(&Extension->DiffAreaFile->WorkItem, VspSignalWorker, &event); InsertTailList(&Extension->WaitingForPageFileSpace, &Extension->DiffAreaFile->WorkItem.List); KeReleaseSpinLock(&Extension->SpinLock, irql); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); VspReleasePagedResource(Extension); } else { KeReleaseSpinLock(&Extension->SpinLock, irql); } _try { r = RtlInsertElementGenericTableFull(Table, TableEntry, sizeof(TRANSLATION_TABLE_ENTRY), NULL, nodeOrParent, searchResult); } _except (EXCEPTION_EXECUTE_HANDLER) { r = NULL; } } return r; } NTSTATUS VspReadDiffAreaTable( IN PVSP_DIFF_AREA_FILE DiffAreaFile ) { PVOLUME_EXTENSION extension = DiffAreaFile->Extension; LONGLONG tableTargetOffset; LONGLONG highestFileOffset, tmp; TRANSLATION_TABLE_ENTRY tableEntry; NTSTATUS status; PVSP_BLOCK_DIFF_AREA diffAreaBlock; ULONG blockOffset; PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY diffAreaTableEntry; PVOID r; KIRQL irql; PTRANSLATION_TABLE_ENTRY backPointer; BOOLEAN b; LONGLONG initialBitmapTargetOffset; tableTargetOffset = DiffAreaFile->TableTargetOffset; highestFileOffset = 0; RtlZeroMemory(&tableEntry, sizeof(tableEntry)); for (;;) { status = VspSynchronousIo(DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, tableTargetOffset, 0); if (!NT_SUCCESS(status)) { return status; } diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress(DiffAreaFile->TableUpdateIrp->MdlAddress); if (!IsEqualGUID(diffAreaBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || diffAreaBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || diffAreaBlock->Header.BlockType != VSP_BLOCK_TYPE_DIFF_AREA || diffAreaBlock->Header.ThisVolumeOffset != tableTargetOffset || diffAreaBlock->Header.NextVolumeOffset == tableTargetOffset) { return STATUS_INVALID_PARAMETER; } if (diffAreaBlock->Header.ThisFileOffset > highestFileOffset) { highestFileOffset = diffAreaBlock->Header.ThisFileOffset; } for (blockOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; blockOffset + sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY) <= BLOCK_SIZE; blockOffset += sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY)) { diffAreaTableEntry = (PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY) ((PCHAR) diffAreaBlock + blockOffset); if (!diffAreaTableEntry->DiffAreaVolumeOffset && !diffAreaTableEntry->Flags) { break; } if (diffAreaTableEntry->SnapshotVolumeOffset >= extension->VolumeSize || diffAreaTableEntry->SnapshotVolumeOffset < 0 || (diffAreaTableEntry->SnapshotVolumeOffset&(BLOCK_SIZE - 1)) || diffAreaTableEntry->DiffAreaFileOffset < 0 || diffAreaTableEntry->DiffAreaVolumeOffset < 0 || (diffAreaTableEntry->DiffAreaVolumeOffset&(BLOCK_SIZE - 1))) { return STATUS_INVALID_PARAMETER; } if (diffAreaTableEntry->Flags& (~VSP_DIFF_AREA_TABLE_ENTRY_FLAG_MOVE_ENTRY)) { return STATUS_INVALID_PARAMETER; } if (diffAreaTableEntry->Flags) { tableEntry.VolumeOffset = diffAreaTableEntry->SnapshotVolumeOffset; tableEntry.TargetObject = extension->Filter->TargetObject; tableEntry.Flags = VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY; tableEntry.TargetOffset = diffAreaTableEntry->DiffAreaFileOffset; } else { if (diffAreaTableEntry->DiffAreaFileOffset > highestFileOffset) { highestFileOffset = diffAreaTableEntry->DiffAreaFileOffset; } tableEntry.VolumeOffset = diffAreaTableEntry->SnapshotVolumeOffset; tableEntry.TargetObject = DiffAreaFile->Filter->TargetObject; tableEntry.Flags = 0; tableEntry.TargetOffset = diffAreaTableEntry->DiffAreaVolumeOffset; } _try { backPointer = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable( &extension->CopyBackPointerTable, &tableEntry); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) { return status; } if (backPointer) { tmp = backPointer->TargetOffset; _try { b = RtlDeleteElementGenericTable( &extension->CopyBackPointerTable, &tableEntry); if (!b) { status = STATUS_INVALID_PARAMETER; } } _except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; status = GetExceptionCode(); } if (!b) { return status; } tableEntry.VolumeOffset = tmp; } r = VspInsertWithAllocateAndWait(extension, &extension->VolumeBlockTable, &tableEntry); if (!r) { return STATUS_INSUFFICIENT_RESOURCES; } if (diffAreaTableEntry->Flags) { tmp = tableEntry.VolumeOffset; tableEntry.VolumeOffset = tableEntry.TargetOffset; tableEntry.TargetOffset = tmp; r = VspInsertWithAllocateAndWait( extension, &extension->CopyBackPointerTable, &tableEntry); if (!r) { return STATUS_INSUFFICIENT_RESOURCES; } } } if (!diffAreaBlock->Header.NextVolumeOffset) { break; } tableTargetOffset = diffAreaBlock->Header.NextVolumeOffset; } initialBitmapTargetOffset = DiffAreaFile->InitialBitmapVolumeOffset; while (initialBitmapTargetOffset) { status = VspSynchronousIo(DiffAreaFile->TableUpdateIrp, DiffAreaFile->Filter->TargetObject, IRP_MJ_READ, initialBitmapTargetOffset, 0); if (!NT_SUCCESS(status)) { break; } diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress(DiffAreaFile->TableUpdateIrp->MdlAddress); if (!IsEqualGUID(diffAreaBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || diffAreaBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || diffAreaBlock->Header.BlockType != VSP_BLOCK_TYPE_INITIAL_BITMAP || diffAreaBlock->Header.ThisVolumeOffset != initialBitmapTargetOffset || diffAreaBlock->Header.NextVolumeOffset == initialBitmapTargetOffset) { break; } if (diffAreaBlock->Header.ThisFileOffset > highestFileOffset) { highestFileOffset = diffAreaBlock->Header.ThisFileOffset; } initialBitmapTargetOffset = diffAreaBlock->Header.NextVolumeOffset; } DiffAreaFile->TableTargetOffset = tableTargetOffset; DiffAreaFile->NextAvailable = highestFileOffset + BLOCK_SIZE; DiffAreaFile->AllocatedFileSize = DiffAreaFile->NextAvailable; DiffAreaFile->NextFreeTableEntryOffset = blockOffset; KeAcquireSpinLock(&extension->Filter->SpinLock, &irql); extension->Filter->UsedVolumeSpace += DiffAreaFile->NextAvailable; extension->Filter->AllocatedVolumeSpace += DiffAreaFile->AllocatedFileSize; KeReleaseSpinLock(&extension->Filter->SpinLock, irql); return STATUS_SUCCESS; } NTSTATUS VspInitializeUnusedAllocationLists( IN PVOLUME_EXTENSION Extension ) { PVSP_DIFF_AREA_FILE diffAreaFile; NTSTATUS status; LONGLONG tableTargetOffset; LONGLONG nextFileOffset; PVSP_BLOCK_DIFF_AREA_LOCATION_DESCRIPTION locationBlock; ULONG blockOffset; PVSP_DIFF_AREA_LOCATION_DESCRIPTOR locationDescriptor; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; LONGLONG delta; KIRQL irql; diffAreaFile = Extension->DiffAreaFile; ASSERT(diffAreaFile); tableTargetOffset = diffAreaFile->DiffAreaLocationDescriptionTargetOffset; nextFileOffset = diffAreaFile->NextAvailable; locationBlock = (PVSP_BLOCK_DIFF_AREA_LOCATION_DESCRIPTION) MmGetMdlVirtualAddress(diffAreaFile->TableUpdateIrp->MdlAddress); for (;;) { status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffAreaFile->Filter->TargetObject, IRP_MJ_READ, tableTargetOffset, 0); if (!NT_SUCCESS(status)) { return status; } if (!IsEqualGUID(locationBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || locationBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || locationBlock->Header.BlockType != VSP_BLOCK_TYPE_DIFF_AREA_LOCATION_DESCRIPTION || locationBlock->Header.ThisVolumeOffset != tableTargetOffset || locationBlock->Header.NextVolumeOffset == tableTargetOffset) { return STATUS_INVALID_PARAMETER; } for (blockOffset = VSP_OFFSET_TO_FIRST_LOCATION_DESCRIPTOR; blockOffset + sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR) <= BLOCK_SIZE; blockOffset += sizeof(VSP_DIFF_AREA_LOCATION_DESCRIPTOR)) { locationDescriptor = (PVSP_DIFF_AREA_LOCATION_DESCRIPTOR) ((PCHAR) locationBlock + blockOffset); if (!locationDescriptor->VolumeOffset) { break; } if (locationDescriptor->VolumeOffset < 0 || locationDescriptor->FileOffset < 0 || locationDescriptor->Length <= 0) { return STATUS_INVALID_PARAMETER; } if (locationDescriptor->FileOffset + locationDescriptor->Length < nextFileOffset) { continue; } if (locationDescriptor->FileOffset < nextFileOffset) { delta = nextFileOffset - locationDescriptor->FileOffset; locationDescriptor->Length -= delta; locationDescriptor->VolumeOffset += delta; locationDescriptor->FileOffset = nextFileOffset; } if (locationDescriptor->FileOffset > nextFileOffset) { diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { return STATUS_INSUFFICIENT_RESOURCES; } diffAreaFileAllocation->Offset = 0; diffAreaFileAllocation->NLength = nextFileOffset - locationDescriptor->FileOffset; InsertTailList(&diffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); nextFileOffset = locationDescriptor->FileOffset; } ASSERT(nextFileOffset == locationDescriptor->FileOffset); diffAreaFileAllocation = (PDIFF_AREA_FILE_ALLOCATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(DIFF_AREA_FILE_ALLOCATION), VOLSNAP_TAG_BIT_HISTORY); if (!diffAreaFileAllocation) { return STATUS_INSUFFICIENT_RESOURCES; } diffAreaFileAllocation->Offset = locationDescriptor->VolumeOffset; diffAreaFileAllocation->NLength = locationDescriptor->Length; InsertTailList(&diffAreaFile->UnusedAllocationList, &diffAreaFileAllocation->ListEntry); nextFileOffset += locationDescriptor->Length; } if (!locationBlock->Header.NextVolumeOffset) { break; } tableTargetOffset = locationBlock->Header.NextVolumeOffset; } delta = nextFileOffset - diffAreaFile->AllocatedFileSize; diffAreaFile->AllocatedFileSize += delta; KeAcquireSpinLock(&Extension->Filter->SpinLock, &irql); Extension->Filter->AllocatedVolumeSpace += delta; KeReleaseSpinLock(&Extension->Filter->SpinLock, irql); status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffAreaFile->Filter->TargetObject, IRP_MJ_READ, diffAreaFile->TableTargetOffset, 0); return status; } VOID VspCleanupDetectedSnapshots( IN PFILTER_EXTENSION Filter ) { KIRQL irql; PLIST_ENTRY l; PVOLUME_EXTENSION extension; while (!IsListEmpty(&Filter->VolumeList)) { KeAcquireSpinLock(&Filter->SpinLock, &irql); l = RemoveHeadList(&Filter->VolumeList); KeReleaseSpinLock(&Filter->SpinLock, irql); extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); KeAcquireSpinLock(&extension->SpinLock, &irql); InterlockedExchange(&extension->IsDead, TRUE); KeReleaseSpinLock(&extension->SpinLock, irql); VspCleanupVolumeSnapshot(extension, NULL, FALSE); RtlDeleteElementGenericTable(&extension->Root->UsedDevnodeNumbers, &extension->DevnodeNumber); IoDeleteDevice(extension->DeviceObject); } } NTSTATUS VspProcessInMemoryCopyOnWrites( IN PVOLUME_EXTENSION Extension ) { PFILTER_EXTENSION filter = Extension->Filter; PVSP_DIFF_AREA_FILE diffAreaFile = Extension->DiffAreaFile; PFILTER_EXTENSION diffFilter = diffAreaFile->Filter; PLIST_ENTRY l; PVSP_COPY_ON_WRITE copyOnWrite; LONGLONG volumeOffset, targetOffset, fileOffset, tmp; PIRP irp; PMDL mdl; LONGLONG t, f; PVSP_BLOCK_DIFF_AREA diffAreaBlock; PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY diffAreaTableEntry; TRANSLATION_TABLE_ENTRY tableEntry; PVOID r; PTRANSLATION_TABLE_ENTRY backPointer; BOOLEAN b; NTSTATUS status; CHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; diffAreaBlock = (PVSP_BLOCK_DIFF_AREA) MmGetMdlVirtualAddress( diffAreaFile->TableUpdateIrp->MdlAddress); while (!IsListEmpty(&filter->CopyOnWriteList)) { l = RemoveHeadList(&filter->CopyOnWriteList); copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); volumeOffset = copyOnWrite->RoundedStart; if (RtlCheckBit(Extension->VolumeBlockBitmap, volumeOffset>>BLOCK_SHIFT)) { ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); continue; } status = VspAllocateDiffAreaSpace(Extension, &targetOffset, &fileOffset, NULL, NULL); if (!NT_SUCCESS(status)) { ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); goto ErrorOut; } mdl = IoAllocateMdl(copyOnWrite->Buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); status = STATUS_INSUFFICIENT_RESOURCES; goto ErrorOut; } irp = IoAllocateIrp(diffFilter->TargetObject->StackSize, FALSE); if (!irp) { IoFreeMdl(mdl); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); status = STATUS_INSUFFICIENT_RESOURCES; goto ErrorOut; } irp->MdlAddress = mdl; MmBuildMdlForNonPagedPool(mdl); status = VspSynchronousIo(irp, diffFilter->TargetObject, IRP_MJ_WRITE, targetOffset, 0); IoFreeIrp(irp); IoFreeMdl(mdl); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); if (!NT_SUCCESS(status)) { goto ErrorOut; } if (diffAreaFile->NextFreeTableEntryOffset + sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY) > BLOCK_SIZE) { status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffFilter->TargetObject, IRP_MJ_WRITE, diffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { goto ErrorOut; } status = VspAllocateDiffAreaSpace(Extension, &t, &f, NULL, NULL); if (!NT_SUCCESS(status)) { goto ErrorOut; } RtlZeroMemory(diffAreaBlock, BLOCK_SIZE); diffAreaBlock->Header.Signature = VSP_DIFF_AREA_FILE_GUID; diffAreaBlock->Header.Version = VOLSNAP_PERSISTENT_VERSION; diffAreaBlock->Header.BlockType = VSP_BLOCK_TYPE_DIFF_AREA; diffAreaBlock->Header.ThisFileOffset = f; diffAreaBlock->Header.ThisVolumeOffset = t; status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffFilter->TargetObject, IRP_MJ_WRITE, t, 0); if (!NT_SUCCESS(status)) { goto ErrorOut; } status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffFilter->TargetObject, IRP_MJ_READ, diffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { goto ErrorOut; } diffAreaBlock->Header.NextVolumeOffset = t; status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffFilter->TargetObject, IRP_MJ_WRITE, diffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { goto ErrorOut; } diffAreaFile->TableTargetOffset = t; status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffFilter->TargetObject, IRP_MJ_READ, diffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { goto ErrorOut; } diffAreaFile->NextFreeTableEntryOffset = VSP_OFFSET_TO_FIRST_TABLE_ENTRY; } diffAreaTableEntry = (PVSP_BLOCK_DIFF_AREA_TABLE_ENTRY) ((PCHAR) diffAreaBlock + diffAreaFile->NextFreeTableEntryOffset); diffAreaFile->NextFreeTableEntryOffset += sizeof(VSP_BLOCK_DIFF_AREA_TABLE_ENTRY); diffAreaTableEntry->SnapshotVolumeOffset = volumeOffset; diffAreaTableEntry->DiffAreaFileOffset = fileOffset; diffAreaTableEntry->DiffAreaVolumeOffset = targetOffset; diffAreaTableEntry->Flags = 0; RtlZeroMemory(&tableEntry, sizeof(TRANSLATION_TABLE_ENTRY)); tableEntry.VolumeOffset = volumeOffset; tableEntry.TargetObject = diffAreaFile->Filter->TargetObject; tableEntry.TargetOffset = targetOffset; _try { backPointer = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable( &Extension->CopyBackPointerTable, &tableEntry); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) { goto ErrorOut; } if (backPointer) { tmp = backPointer->TargetOffset; _try { b = RtlDeleteElementGenericTable( &Extension->CopyBackPointerTable, &tableEntry); if (!b) { status = STATUS_INVALID_PARAMETER; } } _except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; status = GetExceptionCode(); } if (!b) { goto ErrorOut; } tableEntry.VolumeOffset = tmp; } r = VspInsertWithAllocateAndWait(Extension, &Extension->VolumeBlockTable, &tableEntry); if (!r) { status = STATUS_INSUFFICIENT_RESOURCES; goto ErrorOut; } RtlSetBit(Extension->VolumeBlockBitmap, (ULONG) (volumeOffset>>BLOCK_SHIFT)); } status = VspSynchronousIo(diffAreaFile->TableUpdateIrp, diffFilter->TargetObject, IRP_MJ_WRITE, diffAreaFile->TableTargetOffset, 0); if (!NT_SUCCESS(status)) { goto ErrorOut; } status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, FALSE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { goto ErrorOut; } snapshotControlItem->SnapshotControlItemFlags &= ~VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_DETECTION; status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, TRUE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { goto ErrorOut; } return STATUS_SUCCESS; ErrorOut: while (!IsListEmpty(&filter->CopyOnWriteList)) { l = RemoveHeadList(&filter->CopyOnWriteList); copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); } return status; } NTSTATUS VspActivateSnapshots( IN PFILTER_EXTENSION Filter ) { PLIST_ENTRY l; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; BOOLEAN newest; NTSTATUS status; PVOLUME_EXTENSION extension; PVSP_DIFF_AREA_FILE diffAreaFile; ULONG bitmapSize; PVOID bitmapBuffer; PTRANSLATION_TABLE_ENTRY p; PVOID buffer; PMDL mdl; LARGE_INTEGER timeout; BOOLEAN moveEntries; for (l = Filter->SnapshotLookupTableEntries.Flink; l != &Filter->SnapshotLookupTableEntries; l = l->Flink) { lookupEntry = CONTAINING_RECORD(l, VSP_LOOKUP_TABLE_ENTRY, SnapshotFilterListEntry); ASSERT(lookupEntry->DiffAreaFilter); if (l->Flink == &Filter->SnapshotLookupTableEntries) { newest = TRUE; } else { newest = FALSE; } status = VspCreateSnapshotExtension(Filter, lookupEntry, newest); if (!NT_SUCCESS(status)) { VspDeleteControlItemsWithGuid(Filter, NULL, FALSE); VspCleanupDetectedSnapshots(Filter); return status; } if (!newest) { extension = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); diffAreaFile = extension->DiffAreaFile; if (extension->NextDiffAreaFileMap) { status = ZwUnmapViewOfSection( extension->DiffAreaFileMapProcess, extension->NextDiffAreaFileMap); ASSERT(NT_SUCCESS(status)); extension->NextDiffAreaFileMap = NULL; } if (diffAreaFile->TableUpdateIrp) { ExFreePool(MmGetMdlVirtualAddress( diffAreaFile->TableUpdateIrp->MdlAddress)); IoFreeMdl(diffAreaFile->TableUpdateIrp->MdlAddress); IoFreeIrp(diffAreaFile->TableUpdateIrp); diffAreaFile->TableUpdateIrp = NULL; } } } extension = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); extension->VolumeBlockBitmap = (PRTL_BITMAP) ExAllocatePoolWithTag( NonPagedPool, sizeof(RTL_BITMAP), VOLSNAP_TAG_BITMAP); if (!extension->VolumeBlockBitmap) { VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } bitmapSize = (ULONG) ((extension->VolumeSize + BLOCK_SIZE - 1)>> BLOCK_SHIFT); bitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { VspLogError(extension, NULL, VS_CANT_ALLOCATE_BITMAP, STATUS_INSUFFICIENT_RESOURCES, 3, FALSE); ExFreePool(extension->VolumeBlockBitmap); extension->VolumeBlockBitmap = NULL; VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(extension->VolumeBlockBitmap, (PULONG) bitmapBuffer, bitmapSize); RtlClearAllBits(extension->VolumeBlockBitmap); moveEntries = FALSE; status = STATUS_SUCCESS; _try { p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&extension->VolumeBlockTable, TRUE); while (p) { RtlSetBit(extension->VolumeBlockBitmap, (ULONG) (p->VolumeOffset>>BLOCK_SHIFT)); if (p->Flags&VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY) { moveEntries = TRUE; } p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&extension->VolumeBlockTable, FALSE); } if (moveEntries) { p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&extension->VolumeBlockTable, TRUE); while (p) { if (p->Flags&VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY) { RtlClearBit(extension->VolumeBlockBitmap, (ULONG) (p->TargetOffset>>BLOCK_SHIFT)); } p = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable(&extension->VolumeBlockTable, FALSE); } } } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) { VspCleanupDetectedSnapshots(Filter); return status; } extension->IgnorableProduct = (PRTL_BITMAP) ExAllocatePoolWithTag( NonPagedPool, sizeof(RTL_BITMAP), VOLSNAP_TAG_BITMAP); if (!extension->IgnorableProduct) { VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } bitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { VspLogError(extension, NULL, VS_CANT_ALLOCATE_BITMAP, STATUS_INSUFFICIENT_RESOURCES, 4, FALSE); ExFreePool(extension->IgnorableProduct); extension->IgnorableProduct = NULL; VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(extension->IgnorableProduct, (PULONG) bitmapBuffer, bitmapSize); RtlSetAllBits(extension->IgnorableProduct); extension->EmergencyCopyIrp = IoAllocateIrp((CCHAR) extension->Root->StackSize, FALSE); if (!extension->EmergencyCopyIrp) { VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { IoFreeIrp(extension->EmergencyCopyIrp); extension->EmergencyCopyIrp = NULL; VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { ExFreePool(buffer); IoFreeIrp(extension->EmergencyCopyIrp); extension->EmergencyCopyIrp = NULL; VspCleanupDetectedSnapshots(Filter); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(mdl); extension->EmergencyCopyIrp->MdlAddress = mdl; status = VspInitializeUnusedAllocationLists(extension); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 4, FALSE); VspCleanupDetectedSnapshots(Filter); return status; } diffAreaFile = extension->DiffAreaFile; if (diffAreaFile->NextAvailable + extension->DiffAreaFileIncrease > diffAreaFile->AllocatedFileSize) { extension->DetectedNeedForGrow = TRUE; } if (Filter->FirstWriteProcessed) { status = VspProcessInMemoryCopyOnWrites(extension); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 8, FALSE); VspCleanupDetectedSnapshots(Filter); return status; } } InterlockedExchange(&Filter->SnapshotsPresent, TRUE); InterlockedExchange(&Filter->PersistentSnapshots, TRUE); Filter->DiffAreaVolume = extension->DiffAreaFile->Filter; ObReferenceObject(Filter->DeviceObject); timeout.QuadPart = (LONGLONG) -10*1000*1000*120*10; // 20 minutes. KeSetTimer(&Filter->EndCommitTimer, timeout, &Filter->EndCommitTimerDpc); IoInvalidateDeviceRelations(Filter->Pdo, BusRelations); VspCheckMaxSize(Filter); return STATUS_SUCCESS; } VOID VspRemoveLookupEntries( IN PFILTER_EXTENSION Filter ) { PLIST_ENTRY l; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; KeWaitForSingleObject(&Filter->Root->LookupTableMutex, Executive, KernelMode, FALSE, NULL); while (!IsListEmpty(&Filter->SnapshotLookupTableEntries)) { l = RemoveHeadList(&Filter->SnapshotLookupTableEntries); lookupEntry = CONTAINING_RECORD(l, VSP_LOOKUP_TABLE_ENTRY, SnapshotFilterListEntry); ASSERT(lookupEntry->SnapshotFilter); lookupEntry->SnapshotFilter = NULL; if (!lookupEntry->DiffAreaFilter) { ASSERT(!lookupEntry->DiffAreaHandle); RtlDeleteElementGenericTable( &Filter->Root->PersistentSnapshotLookupTable, lookupEntry); } } while (!IsListEmpty(&Filter->DiffAreaLookupTableEntries)) { l = RemoveHeadList(&Filter->DiffAreaLookupTableEntries); lookupEntry = CONTAINING_RECORD(l, VSP_LOOKUP_TABLE_ENTRY, DiffAreaFilterListEntry); ASSERT(lookupEntry->DiffAreaFilter); lookupEntry->DiffAreaFilter = NULL; if (lookupEntry->DiffAreaHandle) { ZwClose(lookupEntry->DiffAreaHandle); lookupEntry->DiffAreaHandle = NULL; } if (!lookupEntry->SnapshotFilter) { RtlDeleteElementGenericTable( &Filter->Root->PersistentSnapshotLookupTable, lookupEntry); } } KeReleaseMutex(&Filter->Root->LookupTableMutex, FALSE); } VOID VspCleanupControlFile( IN PFILTER_EXTENSION Filter ) { LONGLONG controlBlockOffset; PVSP_BLOCK_CONTROL controlBlock; NTSTATUS status; ULONG offset; PVSP_CONTROL_ITEM_SNAPSHOT controlItem; controlBlockOffset = Filter->FirstControlBlockVolumeOffset; controlBlock = (PVSP_BLOCK_CONTROL) MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress); while (controlBlockOffset) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, controlBlockOffset, 0); if (!NT_SUCCESS(status)) { break; } if (!IsEqualGUID(controlBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || controlBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || controlBlock->Header.BlockType != VSP_BLOCK_TYPE_CONTROL || controlBlock->Header.ThisVolumeOffset != controlBlockOffset || controlBlock->Header.NextVolumeOffset == controlBlockOffset) { break; } for (offset = VSP_BYTES_PER_CONTROL_ITEM; offset < BLOCK_SIZE; offset += VSP_BYTES_PER_CONTROL_ITEM) { controlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) ((PCHAR) controlBlock + offset); if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_END) { break; } if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_FREE) { continue; } VspRemoveControlItemInfoFromLookupTable(Filter, controlItem); } controlBlockOffset = controlBlock->Header.NextVolumeOffset; if (!controlBlockOffset) { break; } } VspAcquireNonPagedResource(Filter, NULL, FALSE); VspCreateStartBlock(Filter, 0, Filter->MaximumVolumeSpace); Filter->FirstControlBlockVolumeOffset = 0; VspReleaseNonPagedResource(Filter); } VOID VspWriteTriggerDpc( IN PKDPC TimerDpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->DeleteDiffAreaFiles.Filter; KIRQL irql; PLIST_ENTRY l; PVSP_COPY_ON_WRITE copyOnWrite; ASSERT(context->Type == VSP_CONTEXT_TYPE_PNP_WAIT_TIMER); ExFreePool(context->PnpWaitTimer.Timer); VspFreeContext(filter->Root, context); if (!filter->HoldIncomingWrites || !filter->SnapshotDiscoveryPending) { ObDereferenceObject(filter->DeviceObject); return; } KeAcquireSpinLock(&filter->SpinLock, &irql); if (filter->ActivateStarted || filter->FirstWriteProcessed) { KeReleaseSpinLock(&filter->SpinLock, irql); ObDereferenceObject(filter->DeviceObject); return; } InterlockedExchange(&filter->FirstWriteProcessed, TRUE); while (!IsListEmpty(&filter->CopyOnWriteList)) { l = RemoveHeadList(&filter->CopyOnWriteList); copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); } KeReleaseSpinLock(&filter->SpinLock, irql); ExInitializeWorkItem(&filter->DestroyContext.WorkItem, VspStartCopyOnWriteCache, filter); ExQueueWorkItem(&filter->DestroyContext.WorkItem, DelayedWorkQueue); } VOID VspScheduleWriteTrigger( IN PFILTER_EXTENSION Filter ) { PKTIMER timer; PVSP_CONTEXT context; LARGE_INTEGER timeout; timer = (PKTIMER) ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), VOLSNAP_TAG_SHORT_TERM); if (!timer) { return; } context = VspAllocateContext(Filter->Root); if (!context) { ExFreePool(timer); return; } ObReferenceObject(Filter->DeviceObject); KeInitializeTimer(timer); context->Type = VSP_CONTEXT_TYPE_PNP_WAIT_TIMER; context->PnpWaitTimer.Filter = Filter; KeInitializeDpc(&context->PnpWaitTimer.TimerDpc, VspWriteTriggerDpc, context); context->PnpWaitTimer.Timer = timer; timeout.QuadPart = (LONGLONG) 10*(-10*1000*1000); // 10 seconds. KeSetTimer(timer, timeout, &context->PnpWaitTimer.TimerDpc); } VOID VspDiscoverSnapshots( IN PFILTER_EXTENSION Filter, OUT PBOOLEAN NoControlItems ) { NTSTATUS status; PVOID buffer; PVSP_BLOCK_START startBlock; LONGLONG controlBlockOffset; PVSP_BLOCK_CONTROL controlBlock; ULONG offset; PVSP_CONTROL_ITEM_SNAPSHOT controlItem; BOOLEAN diffAreasFound, snapshotsFound, isComplete; PLIST_ENTRY l; PFILTER_EXTENSION f; KIRQL irql; *NoControlItems = TRUE; if (!Filter->SnapshotOnDiskIrp) { VspCreateSnapshotOnDiskIrp(Filter); if (!Filter->SnapshotOnDiskIrp) { VspResumeVolumeIo(Filter); return; } } status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, 0, 0); if (!NT_SUCCESS(status)) { VspResumeVolumeIo(Filter); return; } buffer = MmGetMdlVirtualAddress(Filter->SnapshotOnDiskIrp->MdlAddress); if (!VspIsNtfsBootSector(Filter, buffer)) { VspResumeVolumeIo(Filter); return; } startBlock = (PVSP_BLOCK_START) ((PCHAR) buffer + VSP_START_BLOCK_OFFSET); if (!IsEqualGUID(startBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID)) { VspResumeVolumeIo(Filter); return; } if (startBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || startBlock->Header.BlockType != VSP_BLOCK_TYPE_START) { VspAcquireNonPagedResource(Filter, NULL, FALSE); VspCreateStartBlock(Filter, 0, 0); Filter->FirstControlBlockVolumeOffset = 0; Filter->MaximumVolumeSpace = 0; VspReleaseNonPagedResource(Filter); VspResumeVolumeIo(Filter); return; } if (startBlock->MaximumDiffAreaSpace >= 0) { Filter->MaximumVolumeSpace = startBlock->MaximumDiffAreaSpace; } controlBlockOffset = startBlock->FirstControlBlockVolumeOffset; if (!controlBlockOffset || controlBlockOffset < 0 || (controlBlockOffset&(BLOCK_SIZE - 1))) { VspResumeVolumeIo(Filter); return; } Filter->FirstControlBlockVolumeOffset = controlBlockOffset; controlBlock = (PVSP_BLOCK_CONTROL) buffer; diffAreasFound = snapshotsFound = FALSE; for (;;) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, controlBlockOffset, 0); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, status, 5, FALSE); VspCleanupControlFile(Filter); break; } if (!IsEqualGUID(controlBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || controlBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || controlBlock->Header.BlockType != VSP_BLOCK_TYPE_CONTROL || controlBlock->Header.ThisVolumeOffset != controlBlockOffset || controlBlock->Header.NextVolumeOffset == controlBlockOffset) { VspLogError(NULL, Filter, VS_ABORT_SNAPSHOTS_DURING_DETECTION, STATUS_INVALID_PARAMETER, 6, FALSE); VspCleanupControlFile(Filter); break; } for (offset = VSP_BYTES_PER_CONTROL_ITEM; offset < BLOCK_SIZE; offset += VSP_BYTES_PER_CONTROL_ITEM) { controlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) ((PCHAR) controlBlock + offset); if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_END) { break; } if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_FREE) { continue; } VspCheckCodeLocked(Filter->Root, TRUE); if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT && !controlItem->SnapshotOrderNumber) { VspLogError(NULL, Filter, VS_PARTIAL_CREATE_REVERTED, STATUS_SUCCESS, 0, FALSE); RtlZeroMemory(controlItem, VSP_BYTES_PER_CONTROL_ITEM); controlItem->ControlItemType = VSP_CONTROL_ITEM_TYPE_FREE; VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, controlBlockOffset, 0); continue; } status = VspAddControlItemInfoToLookupTable(Filter, controlItem, &isComplete); if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_LOSS_OF_CONTROL_ITEM, status, 0, FALSE); RtlZeroMemory(controlItem, VSP_BYTES_PER_CONTROL_ITEM); controlItem->ControlItemType = VSP_CONTROL_ITEM_TYPE_FREE; VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_WRITE, controlBlockOffset, 0); continue; } if (controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_SNAPSHOT) { snapshotsFound = TRUE; if (isComplete) { diffAreasFound = TRUE; } } else { ASSERT(controlItem->ControlItemType == VSP_CONTROL_ITEM_TYPE_DIFF_AREA); diffAreasFound = TRUE; } } controlBlockOffset = controlBlock->Header.NextVolumeOffset; if (!controlBlockOffset) { break; } } if (snapshotsFound) { InterlockedExchange(&Filter->SnapshotDiscoveryPending, TRUE); KeResetEvent(&Filter->EndCommitProcessCompleted); *NoControlItems = FALSE; VspScheduleWriteTrigger(Filter); } else { VspResumeVolumeIo(Filter); } if (!diffAreasFound) { return; } *NoControlItems = FALSE; for (l = Filter->Root->FilterList.Flink; l != &Filter->Root->FilterList; l = l->Flink) { f = CONTAINING_RECORD(l, FILTER_EXTENSION, ListEntry); if (f->SnapshotDiscoveryPending) { if (VspAreSnapshotsComplete(f)) { KeAcquireSpinLock(&f->SpinLock, &irql); f->ActivateStarted = TRUE; if (f->FirstWriteProcessed) { KeReleaseSpinLock(&f->SpinLock, irql); VspPauseVolumeIo(f); } else { KeReleaseSpinLock(&f->SpinLock, irql); } status = VspActivateSnapshots(f); InterlockedExchange(&f->SnapshotDiscoveryPending, FALSE); if (!NT_SUCCESS(status)) { KeSetEvent(&f->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); } VspResumeVolumeIo(f); } } } } VOID VspLaunchOpenControlBlockFileWorker( IN PFILTER_EXTENSION Filter ) { PVSP_CONTEXT context; NTSTATUS status; context = VspAllocateContext(Filter->Root); if (!context) { return; } ObReferenceObject(Filter->DeviceObject); ObReferenceObject(Filter->TargetObject); context->Type = VSP_CONTEXT_TYPE_FILTER; context->Filter.Filter = Filter; ExInitializeWorkItem(&context->WorkItem, VspOpenControlBlockFileWorker, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } NTSTATUS VspQueryDeviceName( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); WCHAR nameBuffer[100]; UNICODE_STRING nameString; PMOUNTDEV_NAME name; if (!Extension->IsPersistent) { return STATUS_INVALID_PARAMETER; } if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) { return STATUS_INVALID_PARAMETER; } swprintf(nameBuffer, L"\\Device\\HarddiskVolumeShadowCopy%d", Extension->VolumeNumber); RtlInitUnicodeString(&nameString, nameBuffer); name = (PMOUNTDEV_NAME) Irp->AssociatedIrp.SystemBuffer; name->NameLength = nameString.Length; Irp->IoStatus.Information = name->NameLength + sizeof(USHORT); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); return STATUS_BUFFER_OVERFLOW; } RtlCopyMemory(name->Name, nameString.Buffer, name->NameLength); return STATUS_SUCCESS; } NTSTATUS VspQueryUniqueId( IN OUT PVOLUME_EXTENSION Extension, IN OUT PIRP Irp ) /*++ Routine Description: This routine is called to query the unique id for this volume. Arguments: Extension - Supplies the volume extension. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PMOUNTDEV_UNIQUE_ID output; if (!Extension->IsPersistent) { return STATUS_INVALID_PARAMETER; } if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { return STATUS_INVALID_PARAMETER; } output = (PMOUNTDEV_UNIQUE_ID) Irp->AssociatedIrp.SystemBuffer; output->UniqueIdLength = sizeof(GUID); Irp->IoStatus.Information = sizeof(USHORT) + output->UniqueIdLength; if (Irp->IoStatus.Information > irpSp->Parameters.DeviceIoControl.OutputBufferLength) { Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); return STATUS_BUFFER_OVERFLOW; } RtlCopyMemory(output->UniqueId, &Extension->SnapshotGuid, sizeof(GUID)); return STATUS_SUCCESS; } NTSTATUS VspQueryStableGuid( IN OUT PVOLUME_EXTENSION Extension, IN OUT PIRP Irp ) /*++ Routine Description: This routine is called to query the stable guid for this volume. Arguments: Extension - Supplies the volume extension. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PMOUNTDEV_STABLE_GUID output; if (!Extension->IsPersistent) { return STATUS_INVALID_PARAMETER; } if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID)) { return STATUS_INVALID_PARAMETER; } output = (PMOUNTDEV_STABLE_GUID) Irp->AssociatedIrp.SystemBuffer; output->StableGuid = Extension->SnapshotGuid; Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID); return STATUS_SUCCESS; } NTSTATUS VspSetGptAttributes( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLUME_SET_GPT_ATTRIBUTES_INFORMATION input = (PVOLUME_SET_GPT_ATTRIBUTES_INFORMATION) Irp->AssociatedIrp.SystemBuffer; CHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; NTSTATUS status; BOOLEAN visible; status = VspCheckSecurity(Extension->Filter, Irp); if (!NT_SUCCESS(status)) { return status; } if (!Extension->IsPersistent) { return STATUS_INVALID_PARAMETER; } if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLUME_SET_GPT_ATTRIBUTES_INFORMATION)) { return STATUS_INVALID_PARAMETER; } if (input->Reserved1 || input->Reserved2 || input->RevertOnClose || input->ApplyToAllConnectedVolumes) { return STATUS_INVALID_PARAMETER; } if (!(input->GptAttributes&GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER)) { return STATUS_INVALID_PARAMETER; } input->GptAttributes &= ~GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER; if (!(input->GptAttributes&GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY)) { return STATUS_INVALID_PARAMETER; } input->GptAttributes &= ~GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY; if (input->GptAttributes&GPT_BASIC_DATA_ATTRIBUTE_HIDDEN) { visible = FALSE; } else { visible = TRUE; } input->GptAttributes &= ~GPT_BASIC_DATA_ATTRIBUTE_HIDDEN; if (input->GptAttributes) { return STATUS_INVALID_PARAMETER; } VspAcquire(Extension->Root); if (visible == Extension->IsVisible) { VspRelease(Extension->Root); return STATUS_SUCCESS; } status = VspIoControlItem(Extension->Filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, FALSE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { VspRelease(Extension->Root); return status; } if (visible) { snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_VISIBLE; } else { snapshotControlItem->SnapshotControlItemFlags &= ~VSP_SNAPSHOT_CONTROL_ITEM_FLAG_VISIBLE; } status = VspIoControlItem(Extension->Filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, TRUE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { VspRelease(Extension->Root); return status; } Extension->IsVisible = visible; if (Extension->IsVisible) { ASSERT(!Extension->MountedDeviceInterfaceName.Buffer); status = IoRegisterDeviceInterface( Extension->DeviceObject, &MOUNTDEV_MOUNTED_DEVICE_GUID, NULL, &Extension->MountedDeviceInterfaceName); if (NT_SUCCESS(status)) { IoSetDeviceInterfaceState( &Extension->MountedDeviceInterfaceName, TRUE); } } else { ASSERT(Extension->MountedDeviceInterfaceName.Buffer); IoSetDeviceInterfaceState(&Extension->MountedDeviceInterfaceName, FALSE); ExFreePool(Extension->MountedDeviceInterfaceName.Buffer); Extension->MountedDeviceInterfaceName.Buffer = NULL; } VspRelease(Extension->Root); return STATUS_SUCCESS; } NTSTATUS VspGetGptAttributes( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) /*++ Routine Description: This routine returns the current GPT attributes definitions for the volume. Arguments: Extension - Supplies the volume extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION output = (PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION) Irp->AssociatedIrp.SystemBuffer; Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } output->GptAttributes = GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER | GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY; if (!Extension->IsVisible) { output->GptAttributes |= GPT_BASIC_DATA_ATTRIBUTE_HIDDEN; } return STATUS_SUCCESS; } NTSTATUS VspGetLengthInfo( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PGET_LENGTH_INFORMATION output = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer; Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < Irp->IoStatus.Information) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } output->Length.QuadPart = Extension->VolumeSize; return STATUS_SUCCESS; } VOID VspDecrementWaitBlock( IN PVOID WaitBlock ) { PVSP_WAIT_BLOCK waitBlock = (PVSP_WAIT_BLOCK) WaitBlock; if (InterlockedDecrement(&waitBlock->RefCount)) { return; } KeSetEvent(&waitBlock->Event, IO_NO_INCREMENT, FALSE); } VOID VspReleaseTableEntry( IN PTEMP_TRANSLATION_TABLE_ENTRY TableEntry ) { PVOLUME_EXTENSION extension = TableEntry->Extension; ULONG targetBit = (ULONG) (TableEntry->VolumeOffset>>BLOCK_SHIFT); KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; PVSP_CONTEXT context; KeAcquireSpinLock(&extension->SpinLock, &irql); RtlSetBit(extension->VolumeBlockBitmap, targetBit); TableEntry->IsComplete = TRUE; KeReleaseSpinLock(&extension->SpinLock, irql); while (!IsListEmpty(&TableEntry->WaitingQueueDpc)) { l = RemoveHeadList(&TableEntry->WaitingQueueDpc); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); context = (PVSP_CONTEXT) workItem->Parameter; if (context->Type == VSP_CONTEXT_TYPE_READ_SNAPSHOT) { context->ReadSnapshot.TargetObject = TableEntry->TargetObject; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = TableEntry->TargetOffset; } workItem->WorkerRoutine(workItem->Parameter); } VspAcquireNonPagedResource(extension, NULL, FALSE); RtlDeleteElementGenericTable(&extension->TempVolumeBlockTable, TableEntry); VspReleaseNonPagedResource(extension); } NTSTATUS VspCopyBlock( IN PVOLUME_EXTENSION Extension, IN LONGLONG Source, IN LONGLONG Target, IN ULONG Length, IN PIRP Irp, OUT PBOOLEAN DontDecrement ) { PFILTER_EXTENSION filter = Extension->Filter; NTSTATUS status, status2; ULONG sourceBit, targetBit; KIRQL irql; PMDL mdl; TEMP_TRANSLATION_TABLE_ENTRY keyTableEntry; PTEMP_TRANSLATION_TABLE_ENTRY tableEntry; PVOID nodeOrParent; TABLE_SEARCH_RESULT searchResult; VSP_WAIT_BLOCK waitBlock; PVSP_WRITE_CONTEXT writeContext; PWORK_QUEUE_ITEM workItem; PIO_STACK_LOCATION irpSp; LONGLONG start, end; PLIST_ENTRY l; TRANSLATION_TABLE_ENTRY key; PVOID r, r2; BOOLEAN b; LIST_ENTRY q; PVSP_CONTEXT context; KEVENT event; PTRANSLATION_TABLE_ENTRY sourceBackPointer; LONGLONG sourceSource; PTRANSLATION_TABLE_ENTRY sourceTableEntry; PVSP_DIFF_AREA_FILE diffAreaFile; BOOLEAN dontNeedWrite; *DontDecrement = FALSE; status = VspSynchronousIo(Irp, filter->DeviceObject, IRP_MJ_READ, Source, Length); if (!NT_SUCCESS(status)) { return status; } if (Length < BLOCK_SIZE) { VspDecrementRefCount(filter); status = VspSynchronousIo(Irp, filter->DeviceObject, IRP_MJ_WRITE, Target, Length); InterlockedIncrement(&filter->RefCount); if (filter->HoldIncomingWrites || !filter->SnapshotsPresent) { *DontDecrement = TRUE; VspDecrementRefCount(filter); return STATUS_INVALID_PARAMETER; } return status; } ASSERT(Length == BLOCK_SIZE); ASSERT(!(Source&(BLOCK_SIZE - 1))); ASSERT(!(Target&(BLOCK_SIZE - 1))); sourceBit = (ULONG) (Source>>BLOCK_SHIFT); targetBit = (ULONG) (Target>>BLOCK_SHIFT); KeAcquireSpinLock(&Extension->SpinLock, &irql); ASSERT(Extension->VolumeBlockBitmap); if (RtlCheckBit(Extension->VolumeBlockBitmap, sourceBit)) { KeReleaseSpinLock(&Extension->SpinLock, irql); VspDecrementRefCount(filter); status = VspSynchronousIo(Irp, filter->DeviceObject, IRP_MJ_WRITE, Target, Length); InterlockedIncrement(&filter->RefCount); if (filter->HoldIncomingWrites || !filter->SnapshotsPresent) { *DontDecrement = TRUE; VspDecrementRefCount(filter); return STATUS_INVALID_PARAMETER; } return status; } if (RtlCheckBit(Extension->VolumeBlockBitmap, targetBit)) { KeReleaseSpinLock(&Extension->SpinLock, irql); } else { KeReleaseSpinLock(&Extension->SpinLock, irql); VspDecrementRefCount(filter); mdl = Irp->MdlAddress; Irp->MdlAddress = NULL; status = VspSynchronousIo(Irp, filter->DeviceObject, IRP_MJ_WRITE, Target, Length); Irp->MdlAddress = mdl; InterlockedIncrement(&filter->RefCount); if (filter->HoldIncomingWrites || !filter->SnapshotsPresent) { *DontDecrement = TRUE; VspDecrementRefCount(filter); return STATUS_INVALID_PARAMETER; } if (!NT_SUCCESS(status)) { return status; } } RtlZeroMemory(&keyTableEntry, sizeof(TEMP_TRANSLATION_TABLE_ENTRY)); keyTableEntry.VolumeOffset = Target; keyTableEntry.Extension = Extension; keyTableEntry.TargetObject = filter->TargetObject; keyTableEntry.TargetOffset = Target; InitializeListHead(&keyTableEntry.WaitingQueueDpc); keyTableEntry.IsMoveEntry = TRUE; keyTableEntry.FileOffset = Source; VspAcquireNonPagedResource(Extension, NULL, FALSE); tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTableFull( &Extension->TempVolumeBlockTable, &keyTableEntry, &nodeOrParent, &searchResult); if (tableEntry) { KeInitializeEvent(&event, NotificationEvent, FALSE); tableEntry->WaitEvent = &event; VspReleaseNonPagedResource(Extension); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); VspAcquireNonPagedResource(Extension, NULL, FALSE); tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTableFull( &Extension->TempVolumeBlockTable, &keyTableEntry, &nodeOrParent, &searchResult); ASSERT(!tableEntry); } ASSERT(!Extension->TempTableEntry); Extension->TempTableEntry = VspAllocateTempTableEntry(Extension->Root); if (!Extension->TempTableEntry) { VspReleaseNonPagedResource(Extension); VspDecrementRefCount(filter); status = VspSynchronousIo(Irp, filter->DeviceObject, IRP_MJ_WRITE, Target, Length); InterlockedIncrement(&filter->RefCount); if (filter->HoldIncomingWrites || !filter->SnapshotsPresent) { *DontDecrement = TRUE; VspDecrementRefCount(filter); return STATUS_INVALID_PARAMETER; } return status; } tableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlInsertElementGenericTableFull( &Extension->TempVolumeBlockTable, &keyTableEntry, sizeof(TEMP_TRANSLATION_TABLE_ENTRY), NULL, nodeOrParent, searchResult); ASSERT(tableEntry); InitializeListHead(&tableEntry->WaitingQueueDpc); KeAcquireSpinLock(&Extension->SpinLock, &irql); RtlClearBit(Extension->VolumeBlockBitmap, targetBit); if (Extension->IgnorableProduct) { RtlClearBit(Extension->IgnorableProduct, targetBit); } KeReleaseSpinLock(&Extension->SpinLock, irql); VspReleaseNonPagedResource(Extension); waitBlock.RefCount = 1; KeInitializeEvent(&waitBlock.Event, NotificationEvent, FALSE); status = STATUS_SUCCESS; KeAcquireSpinLock(&Extension->SpinLock, &irql); for (l = Extension->WriteContextList.Flink; l != &Extension->WriteContextList; l = l->Flink) { writeContext = CONTAINING_RECORD(l, VSP_WRITE_CONTEXT, ListEntry); irpSp = IoGetCurrentIrpStackLocation(writeContext->Irp); start = irpSp->Parameters.Write.ByteOffset.QuadPart; end = start + irpSp->Parameters.Write.Length; if (start >= Target + Length || Target >= end) { continue; } workItem = (PWORK_QUEUE_ITEM) ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_QUEUE_ITEM), VOLSNAP_TAG_SHORT_TERM); if (!workItem) { status = STATUS_INSUFFICIENT_RESOURCES; break; } InterlockedIncrement(&waitBlock.RefCount); ExInitializeWorkItem(workItem, VspDecrementWaitBlock, &waitBlock); InsertTailList(&writeContext->CompletionRoutines, &workItem->List); } KeReleaseSpinLock(&Extension->SpinLock, irql); if (InterlockedDecrement(&waitBlock.RefCount)) { KeWaitForSingleObject(&waitBlock.Event, Executive, KernelMode, FALSE, NULL); } if (!NT_SUCCESS(status)) { VspReleaseTableEntry(tableEntry); return status; } status = VspSynchronousIo(Irp, filter->TargetObject, IRP_MJ_WRITE, Target, Length); if (!NT_SUCCESS(status)) { VspReleaseTableEntry(tableEntry); return status; } RtlZeroMemory(&key, sizeof(TRANSLATION_TABLE_ENTRY)); key.VolumeOffset = Source; key.TargetObject = filter->TargetObject; key.Flags = VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY; key.TargetOffset = Target; VspAcquirePagedResource(Extension, NULL); _try { sourceBackPointer = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable(&Extension->CopyBackPointerTable, &key); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) { VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return status; } if (sourceBackPointer) { sourceSource = sourceBackPointer->TargetOffset; if (sourceSource == Target) { VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return STATUS_SUCCESS; } _try { b = RtlDeleteElementGenericTable(&Extension->CopyBackPointerTable, &key); ASSERT(b); } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) { VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return status; } key.VolumeOffset = Target; key.TargetOffset = sourceSource; _try { r2 = RtlInsertElementGenericTable(&Extension->CopyBackPointerTable, &key, sizeof(key), NULL); if (!r2) { status = STATUS_INSUFFICIENT_RESOURCES; } } _except (EXCEPTION_EXECUTE_HANDLER) { r2 = NULL; status = GetExceptionCode(); } if (!r2) { VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return status; } key.VolumeOffset = sourceSource; key.TargetOffset = Target; _try { sourceTableEntry = (PTRANSLATION_TABLE_ENTRY) RtlLookupElementGenericTable(&Extension->VolumeBlockTable, &key); ASSERT(sourceTableEntry); if (!sourceTableEntry) { status = STATUS_INVALID_PARAMETER; } } _except (EXCEPTION_EXECUTE_HANDLER) { sourceTableEntry = NULL; status = GetExceptionCode(); } if (!sourceTableEntry) { VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return status; } ASSERT(sourceTableEntry->Flags& VSP_TRANSLATION_TABLE_ENTRY_FLAG_COPY_ENTRY); ASSERT(sourceTableEntry->TargetOffset == Source); sourceTableEntry->TargetOffset = Target; } else { _try { r = RtlLookupElementGenericTableFull(&Extension->VolumeBlockTable, &key, &nodeOrParent, &searchResult); } _except (EXCEPTION_EXECUTE_HANDLER) { r = (PVOID) 1; } if (r) { VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return STATUS_SUCCESS; } _try { r = RtlInsertElementGenericTableFull(&Extension->VolumeBlockTable, &key, sizeof(TRANSLATION_TABLE_ENTRY), NULL, nodeOrParent, searchResult); } _except (EXCEPTION_EXECUTE_HANDLER) { r = NULL; } if (!r) { VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return STATUS_SUCCESS; } key.VolumeOffset = Target; key.TargetOffset = Source; _try { r2 = RtlInsertElementGenericTable(&Extension->CopyBackPointerTable, &key, sizeof(TRANSLATION_TABLE_ENTRY), NULL); } _except (EXCEPTION_EXECUTE_HANDLER) { r2 = NULL; } if (!r2) { _try { b = RtlDeleteElementGenericTable(&Extension->VolumeBlockTable, r); } _except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } if (!b) { VspDestroyAllSnapshots(Extension->Filter, NULL, FALSE, FALSE); } VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return STATUS_SUCCESS; } } keyTableEntry.VolumeOffset = Source; VspAcquireNonPagedResource(Extension, NULL, FALSE); r = RtlLookupElementGenericTable(&Extension->TempVolumeBlockTable, &keyTableEntry); if (!r) { KeAcquireSpinLock(&Extension->SpinLock, &irql); ASSERT(Extension->VolumeBlockBitmap); if (RtlCheckBit(Extension->VolumeBlockBitmap, sourceBit)) { r = (PVOID) 1; } KeReleaseSpinLock(&Extension->SpinLock, irql); } diffAreaFile = Extension->DiffAreaFile; if (r) { VspReleaseNonPagedResource(Extension); if (sourceBackPointer) { sourceTableEntry->TargetOffset = Source; _try { b = RtlDeleteElementGenericTable( &Extension->CopyBackPointerTable, r2); if (b) { key.VolumeOffset = Source; key.TargetOffset = sourceSource; r = RtlInsertElementGenericTable( &Extension->CopyBackPointerTable, &key, sizeof(key), NULL); b = r ? TRUE : FALSE; } } _except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } } else { _try { b = RtlDeleteElementGenericTable(&Extension->VolumeBlockTable, r); if (b) { b = RtlDeleteElementGenericTable( &Extension->CopyBackPointerTable, r2); } } _except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } } if (!b) { VspDestroyAllSnapshots(Extension->Filter, NULL, FALSE, FALSE); } VspReleasePagedResource(Extension); VspReleaseTableEntry(tableEntry); return STATUS_SUCCESS; } KeAcquireSpinLock(&Extension->SpinLock, &irql); ASSERT(Extension->VolumeBlockBitmap); RtlSetBit(Extension->VolumeBlockBitmap, sourceBit); KeReleaseSpinLock(&Extension->SpinLock, irql); if (Extension->IsPersistent) { KeInitializeEvent(&event, NotificationEvent, FALSE); tableEntry->WaitEvent = &event; KeAcquireSpinLock(&Extension->SpinLock, &irql); InsertTailList(&diffAreaFile->TableUpdateQueue, &tableEntry->TableUpdateListEntry); tableEntry->InTableUpdateQueue = TRUE; dontNeedWrite = diffAreaFile->TableUpdateInProgress; diffAreaFile->TableUpdateInProgress = TRUE; KeReleaseSpinLock(&Extension->SpinLock, irql); if (!dontNeedWrite) { VspWriteTableUpdates(diffAreaFile); } VspReleaseNonPagedResource(Extension); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); VspAcquireNonPagedResource(Extension, NULL, FALSE); } if (IsListEmpty(&tableEntry->WaitingQueueDpc)) { InitializeListHead(&q); } else { q = tableEntry->WaitingQueueDpc; q.Blink->Flink = &q; q.Flink->Blink = &q; } RtlDeleteElementGenericTable(&Extension->TempVolumeBlockTable, tableEntry); VspReleaseNonPagedResource(Extension); VspReleasePagedResource(Extension); status2 = STATUS_SUCCESS; while (!IsListEmpty(&q)) { l = RemoveHeadList(&tableEntry->WaitingQueueDpc); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); context = (PVSP_CONTEXT) workItem->Parameter; if (context->Type == VSP_CONTEXT_TYPE_READ_SNAPSHOT) { context->ReadSnapshot.TargetObject = tableEntry->TargetObject; context->ReadSnapshot.IsCopyTarget = FALSE; context->ReadSnapshot.TargetOffset = tableEntry->TargetOffset; workItem->WorkerRoutine(workItem->Parameter); } else { if (NT_SUCCESS(status2)) { VspDecrementRefCount(filter); } mdl = Irp->MdlAddress; Irp->MdlAddress = NULL; status = VspSynchronousIo(Irp, filter->DeviceObject, IRP_MJ_WRITE, Target, Length); Irp->MdlAddress = mdl; if (!NT_SUCCESS(status)) { VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); } if (NT_SUCCESS(status2)) { InterlockedIncrement(&filter->RefCount); if (filter->HoldIncomingWrites || !filter->SnapshotsPresent) { *DontDecrement = TRUE; VspDecrementRefCount(filter); status2 = STATUS_INVALID_PARAMETER; } } workItem->WorkerRoutine(workItem->Parameter); } } return status2; } VOID VspCopyDataWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; PFILTER_EXTENSION filter = extension->Filter; PIRP originalIrp = context->Extension.Irp; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(originalIrp); PDISK_COPY_DATA_PARAMETERS input = (PDISK_COPY_DATA_PARAMETERS) originalIrp->AssociatedIrp.SystemBuffer; PVOID buffer = NULL; PMDL mdl = NULL; PIRP irp = NULL; BOOLEAN dontDecrement = FALSE; LONGLONG sourceStart, sourceEnd, targetStart, targetEnd; NTSTATUS status; ULONG delta; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(filter->Root, context); buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); if (!buffer) { status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!mdl) { status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } irp = IoAllocateIrp(filter->DeviceObject->StackSize, FALSE); if (!irp) { status = STATUS_INSUFFICIENT_RESOURCES; goto Finish; } MmBuildMdlForNonPagedPool(mdl); irp->MdlAddress = mdl; sourceStart = input->SourceOffset.QuadPart; sourceEnd = sourceStart + input->CopyLength.QuadPart; targetStart = input->DestinationOffset.QuadPart; targetEnd = targetStart + input->CopyLength.QuadPart; ASSERT(sourceStart < sourceEnd); status = STATUS_SUCCESS; while (sourceStart < sourceEnd) { if (sourceStart&(BLOCK_SIZE - 1)) { delta = (ULONG) (((sourceStart + BLOCK_SIZE)&(~(BLOCK_SIZE - 1))) - sourceStart); } else { delta = BLOCK_SIZE; } if (sourceStart + delta > sourceEnd) { delta = (ULONG) (sourceEnd - sourceStart); } status = VspCopyBlock(extension, sourceStart, targetStart, delta, irp, &dontDecrement); if (!NT_SUCCESS(status)) { break; } sourceStart += delta; targetStart += delta; } Finish: if (irp) { IoFreeIrp(irp); } if (mdl) { IoFreeMdl(mdl); } if (buffer) { ExFreePool(buffer); } if (!dontDecrement) { VspDecrementRefCount(filter); } originalIrp->IoStatus.Information = 0; originalIrp->IoStatus.Status = status; IoCompleteRequest(originalIrp, IO_NO_INCREMENT); } NTSTATUS VspCopyData( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PDISK_COPY_DATA_PARAMETERS input = (PDISK_COPY_DATA_PARAMETERS) Irp->AssociatedIrp.SystemBuffer; LONGLONG sourceStart, sourceEnd, targetStart, targetEnd; PVOLUME_EXTENSION extension; PVSP_CONTEXT context; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(DISK_COPY_DATA_PARAMETERS)) { return STATUS_INVALID_PARAMETER; } if (Filter->IgnoreCopyData || Filter->ProtectedBlocksBitmap) { return STATUS_INVALID_DEVICE_REQUEST; } if (input->Reserved || input->CopyLength.QuadPart < BLOCK_SIZE) { return STATUS_INVALID_PARAMETER; } sourceStart = input->SourceOffset.QuadPart; sourceEnd = sourceStart + input->CopyLength.QuadPart; targetStart = input->DestinationOffset.QuadPart; targetEnd = targetStart + input->CopyLength.QuadPart; if (sourceStart < 0 || targetStart < 0 || input->CopyLength.QuadPart <= 0) { return STATUS_INVALID_PARAMETER; } if (sourceStart < targetEnd && targetStart < sourceEnd) { return STATUS_INVALID_PARAMETER; } if (!Filter->SnapshotsPresent) { return STATUS_INVALID_PARAMETER; } if ((sourceStart&(BLOCK_SIZE - 1)) != (targetStart&(BLOCK_SIZE - 1))) { return STATUS_INVALID_PARAMETER; } InterlockedIncrement(&Filter->RefCount); if (Filter->HoldIncomingWrites || !Filter->SnapshotsPresent) { VspDecrementRefCount(Filter); return STATUS_INVALID_PARAMETER; } extension = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); if (sourceEnd > extension->VolumeSize || targetEnd > extension->VolumeSize) { VspDecrementRefCount(Filter); return STATUS_INVALID_PARAMETER; } context = VspAllocateContext(Filter->Root); if (!context) { VspDecrementRefCount(Filter); return STATUS_INSUFFICIENT_RESOURCES; } context->Type = VSP_CONTEXT_TYPE_EXTENSION; ExInitializeWorkItem(&context->WorkItem, VspCopyDataWorker, context); context->Extension.Extension = extension; context->Extension.Irp = Irp; IoMarkIrpPending(Irp); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); return STATUS_PENDING; } NTSTATUS VspQueryEpic( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVOLSNAP_EPIC output = (PVOLSNAP_EPIC) Irp->AssociatedIrp.SystemBuffer; if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLSNAP_EPIC)) { Irp->IoStatus.Information = 0; return STATUS_INVALID_PARAMETER; } output->EpicNumber = Filter->EpicNumber; Irp->IoStatus.Information = sizeof(VOLSNAP_EPIC); return STATUS_SUCCESS; } NTSTATUS VspQueryOffline( IN PFILTER_EXTENSION Filter ) /*++ Routine Description: This routine returns whether or not the given volume can be taken offline without any resulting volume snapshots being deleted. Arguments: Filter - Supplies the filter extension. Return Value: STATUS_SUCCESS - The volume may be taken offline without any snapshots being deleted. STATUS_UNSUCCESSFUL - The volume being taken offline will result in the destruction of volume snapshots. --*/ { PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; PVOLUME_EXTENSION extension; VspAcquire(Filter->Root); for (l = Filter->DiffAreaFilesOnThisFilter.Flink; l != &Filter->DiffAreaFilesOnThisFilter; l = l->Flink) { diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); extension = diffAreaFile->Extension; ASSERT(extension); ASSERT(extension->Filter); if (extension->Filter != Filter && extension->Filter->PreparedSnapshot != extension) { VspRelease(Filter->Root); return STATUS_UNSUCCESSFUL; } } VspRelease(Filter->Root); return STATUS_SUCCESS; } NTSTATUS VolSnapDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_DEVICE_CONTROL. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; PVOLUME_EXTENSION extension; KEVENT event; BOOLEAN noControlItems; PPARTITION_INFORMATION partInfo; PVSP_CONTEXT context; if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { if (filter->TargetObject->Characteristics&FILE_REMOVABLE_MEDIA) { IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES: VspCheckCodeLocked(filter->Root, FALSE); status = VspFlushAndHoldWrites(filter, Irp); break; case IOCTL_VOLSNAP_RELEASE_WRITES: VspCheckCodeLocked(filter->Root, FALSE); status = VspReleaseWrites(filter); break; case IOCTL_VOLSNAP_PREPARE_FOR_SNAPSHOT: VspCheckCodeLocked(filter->Root, FALSE); status = VspPrepareForSnapshot(filter, Irp); break; case IOCTL_VOLSNAP_ABORT_PREPARED_SNAPSHOT: VspCheckCodeLocked(filter->Root, FALSE); VspAcquireCritical(filter); status = VspAbortPreparedSnapshot(filter, TRUE, FALSE); VspReleaseCritical(filter); break; case IOCTL_VOLSNAP_COMMIT_SNAPSHOT: VspCheckCodeLocked(filter->Root, FALSE); status = VspCommitSnapshot(filter, Irp); break; case IOCTL_VOLSNAP_END_COMMIT_SNAPSHOT: VspCheckCodeLocked(filter->Root, FALSE); VspAcquireCritical(filter); status = VspEndCommitSnapshot(filter, Irp); VspReleaseCritical(filter); break; case IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS: case OLD_IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS: status = VspCheckSecurity(filter, Irp); if (!NT_SUCCESS(status)) { break; } VspCheckCodeLocked(filter->Root, FALSE); status = VspQueryNamesOfSnapshots(filter, Irp); break; case IOCTL_VOLSNAP_CLEAR_DIFF_AREA: VspCheckCodeLocked(filter->Root, FALSE); status = VspClearDiffArea(filter, Irp); break; case IOCTL_VOLSNAP_ADD_VOLUME_TO_DIFF_AREA: VspCheckCodeLocked(filter->Root, FALSE); status = VspAddVolumeToDiffArea(filter, Irp); break; case IOCTL_VOLSNAP_QUERY_DIFF_AREA: case OLD_IOCTL_VOLSNAP_QUERY_DIFF_AREA: status = VspCheckSecurity(filter, Irp); if (!NT_SUCCESS(status)) { break; } VspCheckCodeLocked(filter->Root, FALSE); status = VspQueryDiffArea(filter, Irp); break; case IOCTL_VOLSNAP_SET_MAX_DIFF_AREA_SIZE: VspCheckCodeLocked(filter->Root, FALSE); status = VspSetMaxDiffAreaSize(filter, Irp); break; case IOCTL_VOLSNAP_QUERY_DIFF_AREA_SIZES: case OLD_IOCTL_VOLSNAP_QUERY_DIFF_AREA_SIZES: status = VspCheckSecurity(filter, Irp); if (!NT_SUCCESS(status)) { break; } VspCheckCodeLocked(filter->Root, FALSE); status = VspQueryDiffAreaSizes(filter, Irp); break; case IOCTL_VOLSNAP_DELETE_OLDEST_SNAPSHOT: case IOCTL_VOLSNAP_DELETE_SNAPSHOT: VspCheckCodeLocked(filter->Root, FALSE); status = VspDeleteSnapshotPost(filter, Irp); break; case IOCTL_VOLSNAP_AUTO_CLEANUP: status = VspCheckSecurity(filter, Irp); if (!NT_SUCCESS(status)) { break; } VspCheckCodeLocked(filter->Root, FALSE); status = VspAutoCleanup(filter, Irp); break; case IOCTL_VOLSNAP_QUERY_EPIC: status = VspQueryEpic(filter, Irp); break; case IOCTL_VOLSNAP_QUERY_OFFLINE: VspCheckCodeLocked(filter->Root, FALSE); Irp->IoStatus.Information = 0; status = VspQueryOffline(filter); break; case IOCTL_VOLUME_ONLINE: VspAcquireCritical(filter); VspAcquire(filter->Root); if (filter->IsOnline) { VspRelease(filter->Root); KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); VspReleaseCritical(filter); status = Irp->IoStatus.Status; break; } context = VspAllocateContext(filter->Root); if (!context) { status = STATUS_INSUFFICIENT_RESOURCES; VspRelease(filter->Root); VspReleaseCritical(filter); break; } IoMarkIrpPending(Irp); status = STATUS_PENDING; context->Type = VSP_CONTEXT_TYPE_FILTER; ExInitializeWorkItem(&context->WorkItem, VspOnlineWorker, context); context->Filter.Filter = filter; context->Filter.Irp = Irp; ExQueueWorkItem(&context->WorkItem, CriticalWorkQueue); break; case IOCTL_VOLUME_OFFLINE: VspAcquireCritical(filter); VspAcquire(filter->Root); if (!filter->IsOnline) { VspRelease(filter->Root); KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); VspReleaseCritical(filter); status = Irp->IoStatus.Status; break; } context = VspAllocateContext(filter->Root); if (!context) { status = STATUS_INSUFFICIENT_RESOURCES; VspRelease(filter->Root); VspReleaseCritical(filter); break; } IoMarkIrpPending(Irp); status = STATUS_PENDING; context->Type = VSP_CONTEXT_TYPE_FILTER; ExInitializeWorkItem(&context->WorkItem, VspOfflineWorker, context); context->Filter.Filter = filter; context->Filter.Irp = Irp; ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); break; case IOCTL_DISK_COPY_DATA: status = VspCopyData(filter, Irp); break; default: IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } if (status == STATUS_PENDING) { return status; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); extension = (PVOLUME_EXTENSION) filter; if (!extension->IsStarted || extension->IsPreExposure) { Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NO_SUCH_DEVICE; } status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_VOLSNAP_QUERY_ORIGINAL_VOLUME_NAME: case OLD_IOCTL_VOLSNAP_QUERY_ORIGINAL_VOLUME_NAME: status = VspCheckSecurity(extension->Filter, Irp); if (!NT_SUCCESS(status)) { break; } status = VspQueryOriginalVolumeName(extension, Irp); break; case IOCTL_VOLSNAP_QUERY_CONFIG_INFO: case OLD_IOCTL_VOLSNAP_QUERY_CONFIG_INFO: status = VspCheckSecurity(extension->Filter, Irp); if (!NT_SUCCESS(status)) { break; } status = VspQueryConfigInfo(extension, Irp); break; case IOCTL_VOLSNAP_SET_APPLICATION_INFO: status = VspSetApplicationInfo(extension, Irp); break; case IOCTL_VOLSNAP_QUERY_APPLICATION_INFO: case OLD_IOCTL_VOLSNAP_QUERY_APPLICATION_INFO: status = VspCheckSecurity(extension->Filter, Irp); if (!NT_SUCCESS(status)) { break; } status = VspQueryApplicationInfo(extension, Irp); break; case IOCTL_VOLSNAP_QUERY_DIFF_AREA: case OLD_IOCTL_VOLSNAP_QUERY_DIFF_AREA: status = VspCheckSecurity(extension->Filter, Irp); if (!NT_SUCCESS(status)) { break; } status = VspQueryDiffArea(extension, Irp); break; case IOCTL_VOLSNAP_QUERY_DIFF_AREA_SIZES: case OLD_IOCTL_VOLSNAP_QUERY_DIFF_AREA_SIZES: status = VspCheckSecurity(extension->Filter, Irp); if (!NT_SUCCESS(status)) { break; } status = VspQueryDiffAreaSizes(extension, Irp); break; case IOCTL_DISK_SET_PARTITION_INFO: status = STATUS_SUCCESS; break; case IOCTL_DISK_VERIFY: case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_DISK_CHECK_VERIFY: IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspVolumeRefCountCompletionRoutine, extension, TRUE, TRUE, TRUE); IoMarkIrpPending(Irp); IoCallDriver(extension->Filter->TargetObject, Irp); return STATUS_PENDING; case IOCTL_DISK_GET_LENGTH_INFO: status = VspGetLengthInfo(extension, Irp); break; case IOCTL_DISK_GET_PARTITION_INFO: KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(extension->Filter->TargetObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; if (!NT_SUCCESS(status)) { break; } partInfo = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer; partInfo->PartitionLength.QuadPart = extension->VolumeSize; break; case IOCTL_DISK_IS_WRITABLE: status = STATUS_MEDIA_WRITE_PROTECTED; break; case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: status = VspQueryDeviceName(extension, Irp); break; case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: status = VspQueryUniqueId(extension, Irp); break; case IOCTL_MOUNTDEV_QUERY_STABLE_GUID: status = VspQueryStableGuid(extension, Irp); break; case IOCTL_VOLUME_SUPPORTS_ONLINE_OFFLINE: status = STATUS_SUCCESS; break; case IOCTL_VOLUME_ONLINE: if (extension->IsOffline) { VspSetOfflineBit(extension, FALSE); } status = STATUS_SUCCESS; break; case IOCTL_VOLUME_OFFLINE: if (!extension->IsOffline) { VspSetOfflineBit(extension, TRUE); } status = STATUS_SUCCESS; break; case IOCTL_VOLUME_IS_OFFLINE: if (extension->IsOffline) { status = STATUS_SUCCESS; } else { status = STATUS_UNSUCCESSFUL; } break; case IOCTL_VOLUME_SET_GPT_ATTRIBUTES: status = VspSetGptAttributes(extension, Irp); break; case IOCTL_VOLUME_GET_GPT_ATTRIBUTES: status = VspGetGptAttributes(extension, Irp); break; default: status = STATUS_INVALID_DEVICE_REQUEST; break; } VspDecrementVolumeRefCount(extension); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS VspQueryBusRelations( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { ULONG numVolumes; PLIST_ENTRY l; NTSTATUS status; PDEVICE_RELATIONS deviceRelations, newRelations; ULONG size, i; PVOLUME_EXTENSION extension; if (Filter->SnapshotDiscoveryPending) { numVolumes = 0; } else { numVolumes = 0; for (l = Filter->VolumeList.Flink; l != &Filter->VolumeList; l = l->Flink) { extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); if (l == Filter->VolumeList.Blink && !extension->HasEndCommit && !extension->IsInstalled) { continue; } InterlockedExchange(&extension->AliveToPnp, TRUE); numVolumes++; } if (Filter->PreparedSnapshot && Filter->PreparedSnapshot->IsPreExposure) { InterlockedExchange(&Filter->PreparedSnapshot->AliveToPnp, TRUE); numVolumes++; } } status = Irp->IoStatus.Status; if (!numVolumes) { if (NT_SUCCESS(status)) { return status; } newRelations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), VOLSNAP_TAG_RELATIONS); if (!newRelations) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(newRelations, sizeof(DEVICE_RELATIONS)); newRelations->Count = 0; Irp->IoStatus.Information = (ULONG_PTR) newRelations; while (!IsListEmpty(&Filter->DeadVolumeList)) { l = RemoveHeadList(&Filter->DeadVolumeList); extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); InterlockedExchange(&extension->DeadToPnp, TRUE); } return STATUS_SUCCESS; } if (NT_SUCCESS(status)) { deviceRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information; size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + (numVolumes + deviceRelations->Count)*sizeof(PDEVICE_OBJECT); newRelations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag(PagedPool, size, VOLSNAP_TAG_RELATIONS); if (!newRelations) { for (i = 0; i < deviceRelations->Count; i++) { ObDereferenceObject(deviceRelations->Objects[i]); } ExFreePool(deviceRelations); Irp->IoStatus.Information = 0; return STATUS_INSUFFICIENT_RESOURCES; } newRelations->Count = numVolumes + deviceRelations->Count; RtlCopyMemory(newRelations->Objects, deviceRelations->Objects, deviceRelations->Count*sizeof(PDEVICE_OBJECT)); i = deviceRelations->Count; ExFreePool(deviceRelations); } else { size = sizeof(DEVICE_RELATIONS) + numVolumes*sizeof(PDEVICE_OBJECT); newRelations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag(PagedPool, size, VOLSNAP_TAG_RELATIONS); if (!newRelations) { return STATUS_INSUFFICIENT_RESOURCES; } newRelations->Count = numVolumes; i = 0; } numVolumes = 0; for (l = Filter->VolumeList.Flink; l != &Filter->VolumeList; l = l->Flink) { extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); if (l == Filter->VolumeList.Blink && !extension->HasEndCommit && !extension->IsInstalled) { continue; } newRelations->Objects[i + numVolumes++] = extension->DeviceObject; ObReferenceObject(extension->DeviceObject); } if (Filter->PreparedSnapshot && Filter->PreparedSnapshot->IsPreExposure) { newRelations->Objects[i + numVolumes++] = Filter->PreparedSnapshot->DeviceObject; ObReferenceObject(Filter->PreparedSnapshot->DeviceObject); } Irp->IoStatus.Information = (ULONG_PTR) newRelations; while (!IsListEmpty(&Filter->DeadVolumeList)) { l = RemoveHeadList(&Filter->DeadVolumeList); extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); InterlockedExchange(&extension->DeadToPnp, TRUE); } return STATUS_SUCCESS; } NTSTATUS VspQueryId( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); UNICODE_STRING string; NTSTATUS status; WCHAR buffer[100]; PWSTR id; switch (irpSp->Parameters.QueryId.IdType) { case BusQueryDeviceID: RtlInitUnicodeString(&string, L"STORAGE\\VolumeSnapshot"); break; case BusQueryHardwareIDs: RtlInitUnicodeString(&string, L"STORAGE\\VolumeSnapshot"); break; case BusQueryInstanceID: swprintf(buffer, L"HarddiskVolumeSnapshot%d", Extension->DevnodeNumber); RtlInitUnicodeString(&string, buffer); break; default: return STATUS_NOT_SUPPORTED; } id = (PWSTR) ExAllocatePoolWithTag(PagedPool, string.Length + 2*sizeof(WCHAR), VOLSNAP_TAG_PNP_ID); if (!id) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(id, string.Buffer, string.Length); id[string.Length/sizeof(WCHAR)] = 0; id[string.Length/sizeof(WCHAR) + 1] = 0; Irp->IoStatus.Information = (ULONG_PTR) id; return STATUS_SUCCESS; } VOID VspDeleteDiffAreaFilesWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->DeleteDiffAreaFiles.Filter; NTSTATUS status; UNICODE_STRING name, fileName; OBJECT_ATTRIBUTES oa; HANDLE h, fileHandle; IO_STATUS_BLOCK ioStatus; PFILE_NAMES_INFORMATION fileNamesInfo; CHAR buffer[200]; FILE_DISPOSITION_INFORMATION dispInfo; BOOLEAN restartScan, filesLeft, filesDeleted; GUID guid; ASSERT(context->Type == VSP_CONTEXT_TYPE_DELETE_DA_FILES); KeWaitForSingleObject(&filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); VspAcquireCritical(filter); if (filter->DeleteTimer != context->DeleteDiffAreaFiles.Timer) { VspReleaseCritical(filter); ExFreePool(context->DeleteDiffAreaFiles.Timer); ExFreePool(context->DeleteDiffAreaFiles.Dpc); VspFreeContext(filter->Root, context); ObDereferenceObject(filter->DeviceObject); return; } KeWaitForSingleObject(&filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); filter->DeleteTimer = NULL; ExFreePool(context->DeleteDiffAreaFiles.Timer); ExFreePool(context->DeleteDiffAreaFiles.Dpc); VspFreeContext(filter->Root, context); VspAcquire(filter->Root); if (filter->IsRemoved) { VspRelease(filter->Root); VspReleaseCritical(filter); ObDereferenceObject(filter->DeviceObject); return; } ObReferenceObject(filter->TargetObject); VspRelease(filter->Root); status = VspCreateDiffAreaFileName(filter->TargetObject, NULL, &name, FALSE, NULL); if (!NT_SUCCESS(status)) { ObDereferenceObject(filter->TargetObject); VspReleaseCritical(filter); ObDereferenceObject(filter->DeviceObject); return; } name.Length -= 39*sizeof(WCHAR); InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { ObDereferenceObject(filter->TargetObject); VspReleaseCritical(filter); ObDereferenceObject(filter->DeviceObject); ExFreePool(name.Buffer); return; } fileName.Buffer = &name.Buffer[name.Length/sizeof(WCHAR)]; fileName.Length = 39*sizeof(WCHAR); fileName.MaximumLength = fileName.Length + sizeof(WCHAR); fileNamesInfo = (PFILE_NAMES_INFORMATION) buffer; dispInfo.DeleteFile = TRUE; restartScan = TRUE; filesLeft = FALSE; filesDeleted = FALSE; for (;;) { status = ZwQueryDirectoryFile(h, NULL, NULL, NULL, &ioStatus, fileNamesInfo, 200, FileNamesInformation, TRUE, restartScan ? &fileName : NULL, restartScan); if (!NT_SUCCESS(status)) { break; } restartScan = FALSE; fileName.Length = fileName.MaximumLength = (USHORT) fileNamesInfo->FileNameLength; fileName.Buffer = fileNamesInfo->FileName; InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, h, NULL); status = ZwOpenFile(&fileHandle, DELETE, &oa, &ioStatus, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE); if (!NT_SUCCESS(status)) { filesLeft = TRUE; continue; } filesDeleted = TRUE; if (fileName.Buffer[0] == '{') { fileName.Length = 38*sizeof(WCHAR); status = RtlGUIDFromString(&fileName, &guid); if (NT_SUCCESS(status)) { VspCheckCodeLocked(filter->Root, FALSE); VspDeleteControlItemsWithGuid(filter, &guid, FALSE); } } ZwSetInformationFile(fileHandle, &ioStatus, &dispInfo, sizeof(dispInfo), FileDispositionInformation); ZwClose(fileHandle); } ZwClose(h); ExFreePool(name.Buffer); if (!filesLeft && filesDeleted) { VspAcquireNonPagedResource(filter, NULL, FALSE); ASSERT(!filter->ControlBlockFileHandle); filter->FirstControlBlockVolumeOffset = 0; VspCreateStartBlock(filter, 0, filter->MaximumVolumeSpace); VspReleaseNonPagedResource(filter); } ObDereferenceObject(filter->TargetObject); VspReleaseCritical(filter); ObDereferenceObject(filter->DeviceObject); } NTSTATUS VspPnpStart( IN PVOLUME_EXTENSION Extension ) { PFILTER_EXTENSION filter = Extension->Filter; LONG pastReinit; NTSTATUS status; DEVICE_INSTALL_STATE deviceInstallState; ULONG bytes; KIRQL irql; PVSP_CONTEXT context; PKEVENT event; if (Extension->IsDead) { return STATUS_INVALID_PARAMETER; } InterlockedExchange(&Extension->IsStarted, TRUE); pastReinit = Extension->Root->PastReinit; if (pastReinit && !filter->Root->IsSetup) { status = IoGetDeviceProperty(Extension->DeviceObject, DevicePropertyInstallState, sizeof(deviceInstallState), &deviceInstallState, &bytes); if (!NT_SUCCESS(status) || deviceInstallState != InstallStateInstalled) { return STATUS_SUCCESS; } } VspAcquire(Extension->Root); if (Extension->IsVisible) { ASSERT(!Extension->MountedDeviceInterfaceName.Buffer); status = IoRegisterDeviceInterface( Extension->DeviceObject, &MOUNTDEV_MOUNTED_DEVICE_GUID, NULL, &Extension->MountedDeviceInterfaceName); if (NT_SUCCESS(status)) { IoSetDeviceInterfaceState( &Extension->MountedDeviceInterfaceName, TRUE); } } if (Extension->IsInstalled) { VspRelease(Extension->Root); return STATUS_SUCCESS; } Extension->IsInstalled = TRUE; if (Extension->IsPreExposure) { KeSetEvent(&Extension->PreExposureEvent, IO_NO_INCREMENT, FALSE); VspRelease(Extension->Root); return STATUS_SUCCESS; } if (Extension->ListEntry.Flink != &filter->VolumeList) { KeAcquireSpinLock(&Extension->SpinLock, &irql); if (Extension->IgnorableProduct) { ExFreePool(Extension->IgnorableProduct->Buffer); ExFreePool(Extension->IgnorableProduct); Extension->IgnorableProduct = NULL; } KeReleaseSpinLock(&Extension->SpinLock, irql); VspRelease(Extension->Root); return STATUS_SUCCESS; } if (!KeCancelTimer(&filter->EndCommitTimer)) { VspRelease(Extension->Root); return STATUS_SUCCESS; } if (filter->IsRemoved) { VspRelease(Extension->Root); InterlockedExchange(&filter->HibernatePending, FALSE); KeSetEvent(&filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); ObDereferenceObject(filter->DeviceObject); return STATUS_UNSUCCESSFUL; } ObReferenceObject(filter->TargetObject); VspRelease(Extension->Root); context = VspAllocateContext(filter->Root); if (!context) { InterlockedExchange(&filter->HibernatePending, FALSE); KeSetEvent(&filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); ObDereferenceObject(filter->TargetObject); ObDereferenceObject(filter->DeviceObject); return STATUS_INSUFFICIENT_RESOURCES; } ObReferenceObject(Extension->DeviceObject); context->Type = VSP_CONTEXT_TYPE_EXTENSION; context->Extension.Extension = Extension; context->Extension.Irp = NULL; ExInitializeWorkItem(&context->WorkItem, VspSetIgnorableBlocksInBitmapWorker, context); VspQueueWorkItem(filter->Root, &context->WorkItem, 0); return STATUS_SUCCESS; } NTSTATUS VspGetDeviceObjectPointer( IN PUNICODE_STRING VolumeName, IN ACCESS_MASK DesiredAccess, OUT PFILE_OBJECT* FileObject, OUT PDEVICE_OBJECT* DeviceObject ) { OBJECT_ATTRIBUTES oa; HANDLE h; PFILE_OBJECT fileObject; IO_STATUS_BLOCK ioStatus; NTSTATUS status; InitializeObjectAttributes(&oa, VolumeName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, DesiredAccess, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE); if (!NT_SUCCESS(status)) { return status; } status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, KernelMode, (PVOID *) &fileObject, NULL); if (!NT_SUCCESS(status)) { ZwClose(h); return status; } *FileObject = fileObject; *DeviceObject = IoGetRelatedDeviceObject(fileObject); ZwClose(h); return status; } NTSTATUS VspTakeSnapshot( IN PFILTER_EXTENSION Filter ) { KEVENT event; PMOUNTDEV_NAME name; UCHAR buffer[512]; IO_STATUS_BLOCK ioStatus; NTSTATUS status; UNICODE_STRING volumeName; PFILE_OBJECT fileObject; PDEVICE_OBJECT targetObject; VOLSNAP_PREPARE_INFO prepareInfo[2]; PULONG p; PIRP irp; PIO_STACK_LOCATION irpSp; VOLSNAP_FLUSH_AND_HOLD_INPUT flushInput; KeInitializeEvent(&event, NotificationEvent, FALSE); name = (PMOUNTDEV_NAME) buffer; irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, Filter->TargetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(Filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return status; } volumeName.Buffer = name->Name; volumeName.Length = name->NameLength; volumeName.MaximumLength = name->NameLength; status = VspGetDeviceObjectPointer(&volumeName, FILE_READ_DATA, &fileObject, &targetObject); if (!NT_SUCCESS(status)) { if (status != STATUS_FILE_LOCK_CONFLICT) { return status; } // Contention with AUTOCHK. Open FILE_READ_ATTRIBUTES. status = VspGetDeviceObjectPointer(&volumeName, FILE_READ_ATTRIBUTES, &fileObject, &targetObject); if (!NT_SUCCESS(status)) { return status; } } RtlZeroMemory(prepareInfo, sizeof(VOLSNAP_PREPARE_INFO)); KeInitializeEvent(&event, NotificationEvent, FALSE); p = (PULONG) (prepareInfo + 1); *p = 'NPK '; irp = IoBuildDeviceIoControlRequest( IOCTL_VOLSNAP_PREPARE_FOR_SNAPSHOT, targetObject, prepareInfo, sizeof(VOLSNAP_PREPARE_INFO) + sizeof(ULONG), NULL, 0, FALSE, &event, &ioStatus); if (!irp) { ObDereferenceObject(fileObject); return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; status = IoCallDriver(targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { ObDereferenceObject(fileObject); return status; } status = ExUuidCreate(&flushInput.InstanceId); if (!NT_SUCCESS(status)) { VspAbortPreparedSnapshot(Filter, TRUE, FALSE); ObDereferenceObject(fileObject); return status; } flushInput.NumberOfVolumesToFlush = 1; flushInput.SecondsToHoldFileSystemsTimeout = 60; flushInput.SecondsToHoldIrpsTimeout = 10; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest( IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES, targetObject, &flushInput, sizeof(flushInput), NULL, 0, FALSE, &event, &ioStatus); if (!irp) { VspAbortPreparedSnapshot(Filter, TRUE, FALSE); ObDereferenceObject(fileObject); return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; status = IoCallDriver(targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { VspAbortPreparedSnapshot(Filter, TRUE, FALSE); ObDereferenceObject(fileObject); return status; } KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest( IOCTL_VOLSNAP_COMMIT_SNAPSHOT, targetObject, NULL, 0, NULL, 0, FALSE, &event, &ioStatus); if (!irp) { VspReleaseWrites(Filter); VspAbortPreparedSnapshot(Filter, TRUE, FALSE); ObDereferenceObject(fileObject); return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; status = IoCallDriver(targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } VspReleaseWrites(Filter); if (!NT_SUCCESS(status)) { VspAbortPreparedSnapshot(Filter, TRUE, FALSE); ObDereferenceObject(fileObject); return status; } KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest( IOCTL_VOLSNAP_END_COMMIT_SNAPSHOT, targetObject, NULL, 0, buffer, 200, FALSE, &event, &ioStatus); if (!irp) { ObDereferenceObject(fileObject); return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; status = IoCallDriver(targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } ObDereferenceObject(fileObject); return status; } VOID VspCantHibernatePopUp( IN PFILTER_EXTENSION Filter ) { NTSTATUS status; UNICODE_STRING dosName; status = IoVolumeDeviceToDosName(Filter->DeviceObject, &dosName); if (NT_SUCCESS(status)) { IoRaiseInformationalHardError(STATUS_VOLSNAP_PREPARE_HIBERNATE, &dosName, NULL); ExFreePool(dosName.Buffer); } InterlockedExchange(&Filter->HibernatePending, TRUE); status = VspTakeSnapshot(Filter); if (!NT_SUCCESS(status)) { InterlockedExchange(&Filter->HibernatePending, FALSE); } } NTSTATUS VspCompareFileIds( IN PVOLUME_EXTENSION Extension, IN PUNICODE_STRING FileName ) { PFILTER_EXTENSION filter = Extension->Filter; KEVENT event; PMOUNTDEV_NAME name; UCHAR buffer[512]; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; UNICODE_STRING dirName; OBJECT_ATTRIBUTES oa; HANDLE h; PFILE_ID_FULL_DIR_INFORMATION fileIdDirInfo; LARGE_INTEGER fileId1, fileId2; PWCHAR wstring; KeInitializeEvent(&event, NotificationEvent, FALSE); name = (PMOUNTDEV_NAME) buffer; irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, filter->TargetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return status; } dirName.Length = name->NameLength; dirName.Buffer = name->Name; dirName.Buffer[dirName.Length/sizeof(WCHAR)] = '\\'; dirName.Length += sizeof(WCHAR); dirName.Buffer[dirName.Length/sizeof(WCHAR)] = 0; dirName.MaximumLength = dirName.Length + sizeof(WCHAR); InitializeObjectAttributes(&oa, &dirName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { return status; } fileIdDirInfo = (PFILE_ID_FULL_DIR_INFORMATION) buffer; status = ZwQueryDirectoryFile(h, NULL, NULL, NULL, &ioStatus, fileIdDirInfo, 512, FileIdFullDirectoryInformation, TRUE, FileName, TRUE); ZwClose(h); if (!NT_SUCCESS(status)) { return status; } fileId1 = fileIdDirInfo->FileId; if (Extension->IsOffline) { VspSetOfflineBit(Extension, FALSE); } wstring = (PWCHAR) buffer; swprintf(wstring, L"\\Device\\HarddiskVolumeShadowCopy%d\\", Extension->VolumeNumber); RtlInitUnicodeString(&dirName, wstring); InitializeObjectAttributes(&oa, &dirName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { return status; } fileIdDirInfo = (PFILE_ID_FULL_DIR_INFORMATION) buffer; status = ZwQueryDirectoryFile(h, NULL, NULL, NULL, &ioStatus, fileIdDirInfo, 512, FileIdFullDirectoryInformation, TRUE, FileName, TRUE); ZwClose(h); if (!NT_SUCCESS(status)) { return status; } fileId2 = fileIdDirInfo->FileId; if (fileId1.QuadPart != fileId2.QuadPart) { return STATUS_UNSUCCESSFUL; } return STATUS_SUCCESS; } NTSTATUS VspMarkCrashdumpDirty( IN PVOLUME_EXTENSION Extension ) { NTSTATUS status; CHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; status = VspIoControlItem(Extension->Filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, FALSE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { return status; } snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_CRASHDUMP; status = VspIoControlItem(Extension->Filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &Extension->SnapshotGuid, TRUE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { return status; } return STATUS_SUCCESS; } VOID VspTakeSnapshotWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; NTSTATUS status; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); VspFreeContext(filter->Root, context); status = VspTakeSnapshot(filter); if (!NT_SUCCESS(status)) { VspLogError(NULL, filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 16, FALSE); VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); } ObDereferenceObject(filter->DeviceObject); } NTSTATUS VspHandleDumpUsageNotification( IN PFILTER_EXTENSION Filter, IN BOOLEAN InPath ) { PVOLUME_EXTENSION extension; UNICODE_STRING fileName; NTSTATUS status; PVSP_CONTEXT context; VspAcquire(Filter->Root); if (!InPath) { Filter->UsedForCrashdump = FALSE; VspRelease(Filter->Root); return STATUS_SUCCESS; } if (Filter->UsedForCrashdump) { VspRelease(Filter->Root); return STATUS_SUCCESS; } Filter->UsedForCrashdump = TRUE; if (!Filter->SnapshotsPresent || !Filter->PersistentSnapshots || IsListEmpty(&Filter->VolumeList)) { VspRelease(Filter->Root); return STATUS_SUCCESS; } extension = CONTAINING_RECORD(Filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); ASSERT(extension->IsPersistent); ObReferenceObject(extension->DeviceObject); VspRelease(Filter->Root); if (extension->PageFileCopied) { RtlInitUnicodeString(&fileName, L"pagefile.sys"); status = VspCompareFileIds(extension, &fileName); if (!NT_SUCCESS(status)) { InterlockedExchange(&extension->PageFileCopied, FALSE); } } if (!extension->PageFileCopied) { status = VspMarkCrashdumpDirty(extension); if (NT_SUCCESS(status)) { context = VspAllocateContext(Filter->Root); if (context) { context->Type = VSP_CONTEXT_TYPE_FILTER; ExInitializeWorkItem(&context->WorkItem, VspTakeSnapshotWorker, context); context->Filter.Filter = Filter; ObReferenceObject(Filter->DeviceObject); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } if (!NT_SUCCESS(status)) { VspLogError(NULL, Filter, VS_ABORT_COPY_ON_WRITE_CRASHDUMP_FILES, status, 6, FALSE); VspDestroyAllSnapshots(Filter, NULL, FALSE, FALSE); } } ObDereferenceObject(extension->DeviceObject); return STATUS_SUCCESS; } VOID VspCleanupFilterWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); VspFreeContext(filter->Root, context); VspAcquireCritical(filter); VspAcquire(filter->Root); VspCleanupPreamble(filter); VspRelease(filter->Root); VspCleanupFilter(filter, FALSE, TRUE); VspReleaseCritical(filter); ObDereferenceObject(filter->TargetObject); ObDereferenceObject(filter->DeviceObject); } VOID VspPostCleanupFilter( IN PFILTER_EXTENSION Filter ) { PVSP_CONTEXT context; context = VspAllocateContext(Filter->Root); if (!context) { return; } context->Type = VSP_CONTEXT_TYPE_FILTER; ExInitializeWorkItem(&context->WorkItem, VspCleanupFilterWorker, context); context->Filter.Filter = Filter; ObReferenceObject(Filter->DeviceObject); ObReferenceObject(Filter->TargetObject); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } NTSTATUS VolSnapPnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_PNP. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PDEVICE_OBJECT targetObject; KEVENT event; NTSTATUS status; PVOLUME_EXTENSION extension; BOOLEAN deletePdo; PDEVICE_RELATIONS deviceRelations; PDEVICE_CAPABILITIES capabilities; PKEVENT pevent; PLONG p; if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { if (filter->TargetObject->Characteristics&FILE_REMOVABLE_MEDIA) { targetObject = filter->TargetObject; switch (irpSp->MinorFunction) { case IRP_MN_REMOVE_DEVICE: VspAcquire(filter->Root); if (!filter->NotInFilterList) { RemoveEntryList(&filter->ListEntry); filter->NotInFilterList = TRUE; } VspRelease(filter->Root); IoDetachDevice(targetObject); IoDeleteDevice(filter->DeviceObject); // // Fall through. // case IRP_MN_START_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: case IRP_MN_STOP_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_SURPRISE_REMOVAL: Irp->IoStatus.Status = STATUS_SUCCESS; break; } IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(targetObject, Irp); } switch (irpSp->MinorFunction) { case IRP_MN_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: targetObject = filter->TargetObject; if (irpSp->MinorFunction == IRP_MN_REMOVE_DEVICE) { VspPostCleanupFilter(filter); IoDetachDevice(targetObject); IoDeleteDevice(filter->DeviceObject); } else { VspAcquireCritical(filter); VspAcquire(filter->Root); VspCleanupPreamble(filter); VspRelease(filter->Root); VspCleanupFilter(filter, FALSE, FALSE); VspReleaseCritical(filter); } Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(targetObject, Irp); case IRP_MN_QUERY_DEVICE_RELATIONS: switch (irpSp->Parameters.QueryDeviceRelations.Type) { case BusRelations: break; default: IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); VspAcquire(filter->Root); switch (irpSp->Parameters.QueryDeviceRelations.Type) { case BusRelations: status = VspQueryBusRelations(filter, Irp); break; } VspRelease(filter->Root); break; case IRP_MN_DEVICE_USAGE_NOTIFICATION: KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; if (!NT_SUCCESS(status)) { break; } switch (irpSp->Parameters.UsageNotification.Type) { case DeviceUsageTypePaging: if (irpSp->Parameters.UsageNotification.InPath) { InterlockedExchange(&filter->UsedForPaging, TRUE); } else { InterlockedExchange(&filter->UsedForPaging, FALSE); } break; case DeviceUsageTypeDumpFile: status = VspHandleDumpUsageNotification(filter, irpSp->Parameters.UsageNotification.InPath); break; } break; case IRP_MN_START_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: case IRP_MN_STOP_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_CANCEL_STOP_DEVICE: Irp->IoStatus.Status = STATUS_SUCCESS; // // Fall through. // default: IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); extension = (PVOLUME_EXTENSION) filter; switch (irpSp->MinorFunction) { case IRP_MN_START_DEVICE: status = VspPnpStart(extension); break; case IRP_MN_QUERY_REMOVE_DEVICE: if (extension->Root->IsSetup) { status = STATUS_INVALID_DEVICE_REQUEST; } else { status = STATUS_SUCCESS; } break; case IRP_MN_CANCEL_REMOVE_DEVICE: status = STATUS_SUCCESS; break; case IRP_MN_QUERY_STOP_DEVICE: status = STATUS_UNSUCCESSFUL; break; case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_QUERY_RESOURCES: case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: status = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: InterlockedExchange(&extension->IsStarted, FALSE); VspAcquire(extension->Root); if (extension->MountedDeviceInterfaceName.Buffer) { IoSetDeviceInterfaceState( &extension->MountedDeviceInterfaceName, FALSE); ExFreePool(extension->MountedDeviceInterfaceName.Buffer); extension->MountedDeviceInterfaceName.Buffer = NULL; } VspRelease(extension->Root); if (irpSp->MinorFunction == IRP_MN_REMOVE_DEVICE) { if (extension->DeadToPnp && !extension->DeviceDeleted) { InterlockedExchange(&extension->DeviceDeleted, TRUE); deletePdo = TRUE; } else { deletePdo = FALSE; } } else { deletePdo = FALSE; } if (deletePdo) { VspAcquire(extension->Root); RtlDeleteElementGenericTable( &extension->Root->UsedDevnodeNumbers, &extension->DevnodeNumber); VspRelease(extension->Root); IoDeleteDevice(extension->DeviceObject); } status = STATUS_SUCCESS; break; case IRP_MN_QUERY_DEVICE_RELATIONS: if (irpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) { status = STATUS_NOT_SUPPORTED; break; } deviceRelations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), VOLSNAP_TAG_RELATIONS); if (!deviceRelations) { status = STATUS_INSUFFICIENT_RESOURCES; break; } ObReferenceObject(DeviceObject); deviceRelations->Count = 1; deviceRelations->Objects[0] = DeviceObject; Irp->IoStatus.Information = (ULONG_PTR) deviceRelations; status = STATUS_SUCCESS; break; case IRP_MN_QUERY_CAPABILITIES: capabilities = irpSp->Parameters.DeviceCapabilities.Capabilities; capabilities->SilentInstall = 1; capabilities->SurpriseRemovalOK = 1; capabilities->RawDeviceOK = 1; capabilities->UniqueID = 1; capabilities->Address = extension->VolumeNumber; status = STATUS_SUCCESS; break; case IRP_MN_QUERY_ID: status = VspQueryId(extension, Irp); break; case IRP_MN_QUERY_PNP_DEVICE_STATE: Irp->IoStatus.Information = PNP_DEVICE_DONT_DISPLAY_IN_UI; status = STATUS_SUCCESS; break; case IRP_MN_DEVICE_USAGE_NOTIFICATION: status = STATUS_INVALID_DEVICE_REQUEST; break; default: status = STATUS_NOT_SUPPORTED; break; } if (status == STATUS_NOT_SUPPORTED) { status = Irp->IoStatus.Status; } else { Irp->IoStatus.Status = status; } IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } VOID VspWorkerThread( IN PVOID Context ) /*++ Routine Description: This is a worker thread to process work queue items. Arguments: RootExtension - Supplies the root device extension. Return Value: None. --*/ { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PDO_EXTENSION rootExtension = context->ThreadCreation.RootExtension; ULONG queueNumber = context->ThreadCreation.QueueNumber; KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM queueItem; ASSERT(queueNumber < NUMBER_OF_THREAD_POOLS); ASSERT(context->Type == VSP_CONTEXT_TYPE_THREAD_CREATION); VspFreeContext(rootExtension, context); if (!queueNumber) { KeSetPriorityThread(KeGetCurrentThread(), VSP_HIGH_PRIORITY); } for (;;) { KeWaitForSingleObject(&rootExtension->WorkerSemaphore[queueNumber], Executive, KernelMode, FALSE, NULL); KeAcquireSpinLock(&rootExtension->SpinLock[queueNumber], &irql); if (IsListEmpty(&rootExtension->WorkerQueue[queueNumber])) { KeReleaseSpinLock(&rootExtension->SpinLock[queueNumber], irql); ASSERT(!rootExtension->ThreadsRefCount); PsTerminateSystemThread(STATUS_SUCCESS); return; } l = RemoveHeadList(&rootExtension->WorkerQueue[queueNumber]); KeReleaseSpinLock(&rootExtension->SpinLock[queueNumber], irql); queueItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); queueItem->WorkerRoutine(queueItem->Parameter); } } NTSTATUS VspAddChainToBitmap( IN PFILTER_EXTENSION Filter, IN LONGLONG ChainOffset, IN OUT PRTL_BITMAP Bitmap ) { PVSP_BLOCK_HEADER header; NTSTATUS status; header = (PVSP_BLOCK_HEADER) MmGetMdlVirtualAddress(Filter->SnapshotOnDiskIrp->MdlAddress); while (ChainOffset) { status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, ChainOffset, 0); if (!NT_SUCCESS(status)) { return status; } if (!IsEqualGUID(header->Signature, VSP_DIFF_AREA_FILE_GUID) || header->Version != VOLSNAP_PERSISTENT_VERSION || header->ThisVolumeOffset != ChainOffset || header->NextVolumeOffset == ChainOffset) { return STATUS_INVALID_PARAMETER; } RtlSetBit(Bitmap, (ULONG) (ChainOffset>>BLOCK_SHIFT)); ChainOffset = header->NextVolumeOffset; } return STATUS_SUCCESS; } NTSTATUS VspProtectDiffAreaClusters( IN PFILTER_EXTENSION Filter ) { PRTL_BITMAP bitmap; ULONG bitmapSize; PVOID bitmapBuffer; NTSTATUS status; PLIST_ENTRY l, ll; PVSP_DIFF_AREA_FILE diffAreaFile; PVOLUME_EXTENSION extension; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; PTEMP_TRANSLATION_TABLE_ENTRY tempTableEntry; PTRANSLATION_TABLE_ENTRY tableEntry; KIRQL irql; VspAcquireCritical(Filter); Filter->DeleteTimer = NULL; VspReleaseCritical(Filter); InterlockedExchange(&Filter->Root->VolumesSafeForWriteAccess, TRUE); KeWaitForSingleObject(&Filter->EndCommitProcessCompleted, Executive, KernelMode, FALSE, NULL); bitmap = (PRTL_BITMAP) ExAllocatePoolWithTag(NonPagedPool, sizeof(RTL_BITMAP), VOLSNAP_TAG_BITMAP); if (!bitmap) { return STATUS_INSUFFICIENT_RESOURCES; } bitmapSize = (ULONG) ((VspQueryVolumeSize(Filter) + BLOCK_SIZE - 1)>>BLOCK_SHIFT); if (!bitmapSize) { return STATUS_INSUFFICIENT_RESOURCES; } bitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, (bitmapSize + 8*sizeof(ULONG) - 1)/ (8*sizeof(ULONG))*sizeof(ULONG), VOLSNAP_TAG_BITMAP); if (!bitmapBuffer) { ExFreePool(bitmap); return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(bitmap, (PULONG) bitmapBuffer, bitmapSize); RtlClearAllBits(bitmap); VspAcquire(Filter->Root); VspAcquireNonPagedResource(Filter, NULL, FALSE); if (Filter->FirstControlBlockVolumeOffset) { status = VspAddChainToBitmap(Filter, Filter->FirstControlBlockVolumeOffset, bitmap); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); VspRelease(Filter->Root); ExFreePool(bitmapBuffer); ExFreePool(bitmap); return status; } } VspReleaseNonPagedResource(Filter); for (l = Filter->DiffAreaFilesOnThisFilter.Flink; l != &Filter->DiffAreaFilesOnThisFilter; l = l->Flink) { diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); if (!diffAreaFile->Extension->IsPersistent) { continue; } extension = diffAreaFile->Extension; VspAcquireNonPagedResource(Filter, NULL, FALSE); ASSERT(diffAreaFile->ApplicationInfoTargetOffset); status = VspAddChainToBitmap(Filter, diffAreaFile-> ApplicationInfoTargetOffset, bitmap); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); VspRelease(Filter->Root); ExFreePool(bitmapBuffer); ExFreePool(bitmap); return status; } ASSERT(diffAreaFile->DiffAreaLocationDescriptionTargetOffset); status = VspAddChainToBitmap(Filter, diffAreaFile-> DiffAreaLocationDescriptionTargetOffset, bitmap); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); VspRelease(Filter->Root); ExFreePool(bitmapBuffer); ExFreePool(bitmap); return status; } if (diffAreaFile->InitialBitmapVolumeOffset) { status = VspAddChainToBitmap(Filter, diffAreaFile-> InitialBitmapVolumeOffset, bitmap); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); VspRelease(Filter->Root); ExFreePool(bitmapBuffer); ExFreePool(bitmap); return status; } } ASSERT(diffAreaFile->FirstTableTargetOffset); status = VspAddChainToBitmap(Filter, diffAreaFile-> FirstTableTargetOffset, bitmap); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); VspRelease(Filter->Root); ExFreePool(bitmapBuffer); ExFreePool(bitmap); return status; } VspReleaseNonPagedResource(Filter); VspAcquireNonPagedResource(extension, NULL, FALSE); for (ll = diffAreaFile->UnusedAllocationList.Flink; ll != &diffAreaFile->UnusedAllocationList; ll = ll->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(ll, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength <= 0) { continue; } ASSERT((diffAreaFileAllocation->NLength&(BLOCK_SIZE - 1)) == 0); RtlSetBits(bitmap, (ULONG) (diffAreaFileAllocation->Offset>>BLOCK_SHIFT), (ULONG) (diffAreaFileAllocation->NLength>>BLOCK_SHIFT)); } tempTableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable( &extension->TempVolumeBlockTable, TRUE); while (tempTableEntry) { if (!tempTableEntry->IsMoveEntry && tempTableEntry->TargetOffset) { RtlSetBit(bitmap, (ULONG) (tempTableEntry->TargetOffset>>BLOCK_SHIFT)); } tempTableEntry = (PTEMP_TRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable( &extension->TempVolumeBlockTable, FALSE); } VspReleaseNonPagedResource(extension); status = STATUS_SUCCESS; VspAcquirePagedResource(extension, NULL); _try { tableEntry = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable( &extension->VolumeBlockTable, TRUE); while (tableEntry) { if (!tableEntry->Flags) { RtlSetBit(bitmap, ( ULONG) (tableEntry->TargetOffset>>BLOCK_SHIFT)); } tableEntry = (PTRANSLATION_TABLE_ENTRY) RtlEnumerateGenericTable( &extension->VolumeBlockTable, FALSE); } } _except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } VspReleasePagedResource(extension); if (!NT_SUCCESS(status)) { VspRelease(Filter->Root); ExFreePool(bitmapBuffer); ExFreePool(bitmap); return status; } } VspPauseVolumeIo(Filter); KeAcquireSpinLock(&Filter->SpinLock, &irql); InterlockedExchangePointer((PVOID*) &Filter->ProtectedBlocksBitmap, bitmap); KeReleaseSpinLock(&Filter->SpinLock, irql); VspResumeVolumeIo(Filter); VspRelease(Filter->Root); return STATUS_SUCCESS; } NTSTATUS VspOpenAndValidateDiffAreaFiles( IN PFILTER_EXTENSION Filter ) { NTSTATUS status; PVOID buffer; PVSP_BLOCK_START startBlock; HANDLE h, hh; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; UNICODE_STRING fileName; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK ioStatus; BOOLEAN checkUnused; PVOLUME_EXTENSION extension; PVSP_CONTEXT context; KIRQL irql; h = InterlockedExchangePointer(&Filter->ControlBlockFileHandle, NULL); if (h) { ZwClose(h); } if (!Filter->FirstControlBlockVolumeOffset) { return STATUS_SUCCESS; } status = VspOpenControlBlockFile(Filter); if (!NT_SUCCESS(status)) { return status; } VspAcquireNonPagedResource(Filter, NULL, FALSE); status = VspSynchronousIo(Filter->SnapshotOnDiskIrp, Filter->TargetObject, IRP_MJ_READ, 0, 0); if (!NT_SUCCESS(status)) { VspReleaseNonPagedResource(Filter); return status; } buffer = MmGetMdlVirtualAddress(Filter->SnapshotOnDiskIrp->MdlAddress); if (!VspIsNtfsBootSector(Filter, buffer)) { VspReleaseNonPagedResource(Filter); return STATUS_INVALID_PARAMETER; } startBlock = (PVSP_BLOCK_START) ((PCHAR) buffer + VSP_START_BLOCK_OFFSET); if (!IsEqualGUID(startBlock->Header.Signature, VSP_DIFF_AREA_FILE_GUID) || startBlock->Header.Version != VOLSNAP_PERSISTENT_VERSION || startBlock->Header.BlockType != VSP_BLOCK_TYPE_START) { VspReleaseNonPagedResource(Filter); return STATUS_INVALID_PARAMETER; } VspReleaseNonPagedResource(Filter); h = VspPinBootStat(Filter); if (h) { hh = InterlockedExchangePointer(&Filter->BootStatHandle, h); if (hh) { ZwClose(hh); } } VspAcquire(Filter->Root); for (l = Filter->DiffAreaFilesOnThisFilter.Flink; l != &Filter->DiffAreaFilesOnThisFilter; l = l->Flink) { diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); diffAreaFile->ValidateHandleNeeded = TRUE; } VspRelease(Filter->Root); for (;;) { VspAcquire(Filter->Root); for (l = Filter->DiffAreaFilesOnThisFilter.Flink; l != &Filter->DiffAreaFilesOnThisFilter; l = l->Flink) { diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); if (diffAreaFile->ValidateHandleNeeded) { diffAreaFile->ValidateHandleNeeded = FALSE; break; } } if (l == &Filter->DiffAreaFilesOnThisFilter) { VspRelease(Filter->Root); break; } extension = diffAreaFile->Extension; status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { VspRelease(Filter->Root); continue; } VspRelease(Filter->Root); status = VspCreateDiffAreaFileName(Filter->TargetObject, extension, &fileName, FALSE, NULL); if (!NT_SUCCESS(status)) { VspDecrementVolumeRefCount(extension); return status; } ZwClose(diffAreaFile->FileHandle); InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &oa, &ioStatus, 0, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION); ExFreePool(fileName.Buffer); if (!NT_SUCCESS(status)) { VspAcquire(Filter->Root); diffAreaFile->FileHandle = NULL; VspRelease(Filter->Root); VspDecrementVolumeRefCount(extension); return status; } status = VspPinFile(Filter->TargetObject, h); if (!NT_SUCCESS(status)) { ZwClose(h); VspAcquire(Filter->Root); diffAreaFile->FileHandle = NULL; VspRelease(Filter->Root); VspDecrementVolumeRefCount(extension); return status; } KeAcquireSpinLock(&extension->Filter->SpinLock, &irql); if (extension->ListEntry.Flink == &extension->Filter->VolumeList) { checkUnused = TRUE; } else { checkUnused = FALSE; } KeReleaseSpinLock(&extension->Filter->SpinLock, irql); status = VspCheckDiffAreaFileLocation( Filter, h, diffAreaFile->ApplicationInfoTargetOffset, diffAreaFile->FirstTableTargetOffset, diffAreaFile->DiffAreaLocationDescriptionTargetOffset, diffAreaFile->InitialBitmapVolumeOffset, checkUnused); if (!NT_SUCCESS(status)) { ZwClose(h); VspAcquire(Filter->Root); diffAreaFile->FileHandle = NULL; VspRelease(Filter->Root); VspDecrementVolumeRefCount(extension); return status; } InterlockedExchangePointer(&diffAreaFile->FileHandle, h); if (InterlockedExchange(&extension->GrowFailed, FALSE)) { context = VspAllocateContext(extension->Root); if (context) { KeAcquireSpinLock(&extension->SpinLock, &irql); ASSERT(!extension->GrowDiffAreaFilePending); ASSERT(IsListEmpty(&extension->WaitingForDiffAreaSpace)); extension->GrowDiffAreaFilePending = TRUE; extension->PastFileSystemOperations = FALSE; KeReleaseSpinLock(&extension->SpinLock, irql); context->Type = VSP_CONTEXT_TYPE_GROW_DIFF_AREA; ExInitializeWorkItem(&context->WorkItem, VspGrowDiffArea, context); context->GrowDiffArea.Extension = extension; ObReferenceObject(extension->DeviceObject); ASSERT(extension->OkToGrowDiffArea); VspQueueWorkItem(extension->Root, &context->WorkItem, 0); } } VspDecrementVolumeRefCount(extension); } return STATUS_SUCCESS; } BOOLEAN VspIsFileSystemLocked( IN PFILTER_EXTENSION Filter ) { KEVENT event; PMOUNTDEV_NAME name; CHAR buffer[512]; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; UNICODE_STRING volumeName; OBJECT_ATTRIBUTES oa; HANDLE h; KeInitializeEvent(&event, NotificationEvent, FALSE); name = (PMOUNTDEV_NAME) buffer; irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, Filter->TargetObject, NULL, 0, name, 500, FALSE, &event, &ioStatus); if (!irp) { return FALSE; } status = IoCallDriver(Filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return FALSE; } volumeName.Length = volumeName.MaximumLength = name->NameLength; volumeName.Buffer = name->Name; InitializeObjectAttributes(&oa, &volumeName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(status)) { ZwClose(h); } if (status == STATUS_ACCESS_DENIED) { return TRUE; } return FALSE; } NTSTATUS VolSnapTargetDeviceNotification( IN PVOID NotificationStructure, IN PVOID Filter ) { PTARGET_DEVICE_REMOVAL_NOTIFICATION notification = (PTARGET_DEVICE_REMOVAL_NOTIFICATION) NotificationStructure; PFILTER_EXTENSION filter = (PFILTER_EXTENSION) Filter; BOOLEAN cleanupBitmap = FALSE; NTSTATUS status; KIRQL irql; PRTL_BITMAP bitmap; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; PFILTER_EXTENSION f; LIST_ENTRY listOfDiffAreaFilesToClose; LIST_ENTRY listOfDeviceObjectsToDelete; HANDLE h, hh; PVOLUME_EXTENSION extension; BOOLEAN b, persistentOk; if (IsEqualGUID(notification->Event, GUID_TARGET_DEVICE_REMOVE_COMPLETE)) { if (filter->TargetDeviceNotificationEntry) { IoUnregisterPlugPlayNotification( filter->TargetDeviceNotificationEntry); filter->TargetDeviceNotificationEntry = NULL; } return STATUS_SUCCESS; } if (IsEqualGUID(notification->Event, GUID_IO_VOLUME_MOUNT)) { if (!filter->ProtectedBlocksBitmap) { return STATUS_SUCCESS; } status = VspOpenAndValidateDiffAreaFiles(filter); if (!NT_SUCCESS(status)) { persistentOk = FALSE; cleanupBitmap = TRUE; goto DeleteSnapshots; } KeAcquireSpinLock(&filter->SpinLock, &irql); bitmap = (PRTL_BITMAP) InterlockedExchangePointer( (PVOID*) &filter->ProtectedBlocksBitmap, NULL); if (bitmap) { ExFreePool(bitmap->Buffer); ExFreePool(bitmap); } KeReleaseSpinLock(&filter->SpinLock, irql); return STATUS_SUCCESS; } if (!IsEqualGUID(notification->Event, GUID_IO_VOLUME_DISMOUNT)) { return STATUS_SUCCESS; } if (VspIsFileSystemLocked(filter)) { return STATUS_SUCCESS; } if (filter->FirstControlBlockVolumeOffset) { persistentOk = TRUE; } else { persistentOk = FALSE; } if (persistentOk) { if (!filter->ProtectedBlocksBitmap) { status = VspProtectDiffAreaClusters(filter); if (!NT_SUCCESS(status)) { persistentOk = FALSE; } } } DeleteSnapshots: InitializeListHead(&listOfDiffAreaFilesToClose); InitializeListHead(&listOfDeviceObjectsToDelete); VspAcquire(filter->Root); for (;;) { for (l = filter->DiffAreaFilesOnThisFilter.Flink; l != &filter->DiffAreaFilesOnThisFilter; l = l->Flink) { diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); if (!persistentOk || !diffAreaFile->Extension->IsPersistent) { break; } } if (l == &filter->DiffAreaFilesOnThisFilter) { break; } f = diffAreaFile->Extension->Filter; VspLogError(diffAreaFile->Extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_DISMOUNT, STATUS_SUCCESS, 1, TRUE); VspAbortPreparedSnapshot(f, FALSE, FALSE); b = FALSE; while (!IsListEmpty(&f->VolumeList)) { extension = CONTAINING_RECORD(f->VolumeList.Flink, VOLUME_EXTENSION, ListEntry); if (persistentOk && extension->IsPersistent) { break; } VspDeleteOldestSnapshot(f, &listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete, FALSE, TRUE); b = TRUE; } if (b) { IoInvalidateDeviceRelations(f->Pdo, BusRelations); } } VspAbortPreparedSnapshot(filter, FALSE, FALSE); if (filter->PersistentSnapshots && !persistentOk && !IsListEmpty(&filter->VolumeList)) { extension = CONTAINING_RECORD(filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); VspLogError(extension, extension->Filter, VS_ABORT_SNAPSHOTS_DISMOUNT_ORIGINAL, STATUS_SUCCESS, 2, TRUE); b = FALSE; while (!IsListEmpty(&filter->VolumeList)) { VspDeleteOldestSnapshot(filter, &listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete, FALSE, TRUE); b = TRUE; } if (b) { IoInvalidateDeviceRelations(filter->Pdo, BusRelations); } } if (persistentOk) { hh = NULL; } else { hh = InterlockedExchangePointer(&filter->BootStatHandle, NULL); } if (cleanupBitmap) { KeAcquireSpinLock(&filter->SpinLock, &irql); bitmap = (PRTL_BITMAP) InterlockedExchangePointer( (PVOID*) &filter->ProtectedBlocksBitmap, NULL); if (bitmap) { ExFreePool(bitmap->Buffer); ExFreePool(bitmap); } KeReleaseSpinLock(&filter->SpinLock, irql); } VspRelease(filter->Root); VspCloseDiffAreaFiles(&listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete); if (persistentOk) { h = NULL; } else { h = InterlockedExchangePointer(&filter->ControlBlockFileHandle, NULL); VspAcquireNonPagedResource(filter, NULL, FALSE); filter->FirstControlBlockVolumeOffset = 0; if (h) { VspCreateStartBlock(filter, 0, filter->MaximumVolumeSpace); } VspReleaseNonPagedResource(filter); } if (h) { ZwClose(h); } if (hh) { ZwClose(hh); } return STATUS_SUCCESS; } NTSTATUS VolSnapVolumeDeviceNotification( IN PVOID NotificationStructure, IN PVOID RootExtension ) /*++ Routine Description: This routine is called whenever a volume comes or goes. Arguments: NotificationStructure - Supplies the notification structure. RootExtension - Supplies the root extension. Return Value: NTSTATUS --*/ { PDEVICE_INTERFACE_CHANGE_NOTIFICATION notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationStructure; PDO_EXTENSION root = (PDO_EXTENSION) RootExtension; BOOLEAN errorMode; NTSTATUS status; PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; PFILTER_EXTENSION filter; if (!IsEqualGUID(notification->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) { return STATUS_SUCCESS; } errorMode = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread()); PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE); status = IoGetDeviceObjectPointer(notification->SymbolicLinkName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject); if (!NT_SUCCESS(status)) { PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), errorMode); return STATUS_SUCCESS; } if (fileObject->DeviceObject->Characteristics&FILE_REMOVABLE_MEDIA) { ObDereferenceObject(fileObject); PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), errorMode); return STATUS_SUCCESS; } VspAcquire(root); filter = VspFindFilter(root, NULL, NULL, fileObject); if (!filter || filter->TargetDeviceNotificationEntry) { ObDereferenceObject(fileObject); PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), errorMode); VspRelease(root); return STATUS_SUCCESS; } ObReferenceObject(filter->DeviceObject); VspRelease(root); status = IoRegisterPlugPlayNotification( EventCategoryTargetDeviceChange, 0, fileObject, root->DriverObject, VolSnapTargetDeviceNotification, filter, &filter->TargetDeviceNotificationEntry); ObDereferenceObject(filter->DeviceObject); ObDereferenceObject(fileObject); PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), errorMode); return STATUS_SUCCESS; } VOID VspWaitToRegisterWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PDO_EXTENSION rootExtension = context->RootExtension.RootExtension; ASSERT(context->Type == VSP_CONTEXT_TYPE_ROOT_EXTENSION); VspFreeContext(rootExtension, context); VspWaitForVolumesSafeForWriteAccess(rootExtension); if (rootExtension->NotificationEntry) { return; } IoRegisterPlugPlayNotification( EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, (PVOID) &GUID_IO_VOLUME_DEVICE_INTERFACE, rootExtension->DriverObject, VolSnapVolumeDeviceNotification, rootExtension, &rootExtension->NotificationEntry); } VOID VspDriverBootReinit( IN PDRIVER_OBJECT DriverObject, IN PVOID RootExtension, IN ULONG Count ) /*++ Routine Description: This routine is called after all of the boot drivers are loaded and it checks to make sure that we did not boot off of the stale half of a mirror. Arguments: DriverObject - Supplies the drive object. RootExtension - Supplies the root extension. Count - Supplies the count. Return Value: None. --*/ { PDO_EXTENSION rootExtension = (PDO_EXTENSION) RootExtension; PVSP_CONTEXT context; KeSetEvent(&rootExtension->PastBootReinit, IO_NO_INCREMENT, FALSE); } VOID VspDriverReinit( IN PDRIVER_OBJECT DriverObject, IN PVOID RootExtension, IN ULONG Count ) /*++ Routine Description: This routine is called after all of the boot drivers are loaded and it checks to make sure that we did not boot off of the stale half of a mirror. Arguments: DriverObject - Supplies the drive object. RootExtension - Supplies the root extension. Count - Supplies the count. Return Value: None. --*/ { PDO_EXTENSION rootExtension = (PDO_EXTENSION) RootExtension; PLIST_ENTRY l; PFILTER_EXTENSION filter; HANDLE h, hh; PVSP_CONTEXT context; VspAcquire(rootExtension); for (l = rootExtension->FilterList.Flink; l != &rootExtension->FilterList; l = l->Flink) { filter = CONTAINING_RECORD(l, FILTER_EXTENSION, ListEntry); if (filter->Pdo->Flags&DO_SYSTEM_BOOT_PARTITION) { break; } } if (l == &rootExtension->FilterList || IsListEmpty(&filter->VolumeList)) { VspRelease(rootExtension); } else { VspRelease(rootExtension); h = VspPinBootStat(filter); if (h) { hh = InterlockedExchangePointer(&filter->BootStatHandle, h); if (hh) { ZwClose(hh); } } } InterlockedExchange(&rootExtension->PastReinit, TRUE); context = VspAllocateContext(rootExtension); if (!context) { return; } context->Type = VSP_CONTEXT_TYPE_ROOT_EXTENSION; context->RootExtension.RootExtension = rootExtension; ExInitializeWorkItem(&context->WorkItem, VspWaitToRegisterWorker, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } RTL_GENERIC_COMPARE_RESULTS VspSnapshotLookupCompareRoutine( IN PRTL_GENERIC_TABLE Table, IN PVOID First, IN PVOID Second ) { SIZE_T r; PUCHAR p, q; r = RtlCompareMemory(First, Second, sizeof(GUID)); if (r == sizeof(GUID)) { return GenericEqual; } p = (PUCHAR) First; q = (PUCHAR) Second; if (p[r] < q[r]) { return GenericLessThan; } return GenericGreaterThan; } RTL_GENERIC_COMPARE_RESULTS VspUsedDevnodeCompareRoutine( IN PRTL_GENERIC_TABLE Table, IN PVOID First, IN PVOID Second ) { PULONG f = (PULONG) First; PULONG s = (PULONG) Second; if (*f == *s) { return GenericEqual; } if (*f < *s) { return GenericLessThan; } return GenericGreaterThan; } VOID VspSnapshotLookupFreeRoutine( IN PRTL_GENERIC_TABLE Table, IN PVOID Buffer ) { ExFreePool(Buffer); } PVOID VspSnapshotLookupAllocateRoutine( IN PRTL_GENERIC_TABLE Table, IN CLONG Size ) { return ExAllocatePoolWithTag(PagedPool, Size, VOLSNAP_TAG_LOOKUP); } NTSTATUS VspCheckForNtfs( IN PVOLUME_EXTENSION Extension ) { WCHAR buffer[100]; UNICODE_STRING dirName; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE h; IO_STATUS_BLOCK ioStatus; BOOLEAN isNtfs; if (Extension->IsOffline) { VspSetOfflineBit(Extension, FALSE); } swprintf(buffer, L"\\Device\\HarddiskVolumeShadowCopy%d\\", Extension->VolumeNumber); RtlInitUnicodeString(&dirName, buffer); InitializeObjectAttributes(&oa, &dirName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { return status; } status = VspIsNtfs(h, &isNtfs); ZwClose(h); if (!NT_SUCCESS(status)) { return status; } return isNtfs ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } VOID VspHandleHibernatePost( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PVOLUME_EXTENSION extension = context->Extension.Extension; PIRP irp = context->Extension.Irp; PFILTER_EXTENSION filter = extension->Filter; NTSTATUS status; UNICODE_STRING fileName; ASSERT(context->Type == VSP_CONTEXT_TYPE_EXTENSION); VspFreeContext(extension->Root, context); status = VspCheckForNtfs(extension); if (!NT_SUCCESS(status)) { VspLogError(NULL, filter, VS_ABORT_NON_NTFS_HIBER_VOLUME, status, 0, FALSE); VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); status = STATUS_SUCCESS; goto Finish; } if (extension->HiberFileCopied) { RtlInitUnicodeString(&fileName, L"hiberfil.sys"); status = VspCompareFileIds(extension, &fileName); if (NT_SUCCESS(status)) { goto Finish; } } VspCantHibernatePopUp(filter); status = STATUS_INVALID_DEVICE_REQUEST; Finish: irp->IoStatus.Status = status; PoStartNextPowerIrp(irp); IoCompleteRequest(irp, IO_NO_INCREMENT); } NTSTATUS VspHandleHibernateNotification( IN PVOLUME_EXTENSION Extension, IN PIRP Irp ) { PFILTER_EXTENSION filter = Extension->Filter; KIRQL irql; PVSP_CONTEXT context; if (!(filter->Pdo->Flags&DO_SYSTEM_BOOT_PARTITION)) { return STATUS_SUCCESS; } KeAcquireSpinLock(&filter->SpinLock, &irql); if (Extension->ListEntry.Flink != &filter->VolumeList) { KeReleaseSpinLock(&filter->SpinLock, irql); return STATUS_SUCCESS; } KeReleaseSpinLock(&filter->SpinLock, irql); context = VspAllocateContext(Extension->Root); if (!context) { return STATUS_INSUFFICIENT_RESOURCES; } IoMarkIrpPending(Irp); context->Type = VSP_CONTEXT_TYPE_EXTENSION; ExInitializeWorkItem(&context->WorkItem, VspHandleHibernatePost, context); context->Extension.Extension = Extension; context->Extension.Irp = Irp; ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); return STATUS_PENDING; } VOID VspDismountCleanupOnWrite( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->DismountCleanupOnWrite.Filter; PIRP irp = context->DismountCleanupOnWrite.Irp; PVOLUME_EXTENSION e; KIRQL irql; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; PFILTER_EXTENSION f, df; PRTL_BITMAP bitmap; ASSERT(context->Type == VSP_CONTEXT_TYPE_DISMOUNT_CLEANUP); for (;;) { f = df = NULL; e = NULL; KeAcquireSpinLock(&filter->SpinLock, &irql); for (l = filter->DiffAreaFilesOnThisFilter.Flink; l != &filter->DiffAreaFilesOnThisFilter; l = l->Flink) { diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); f = diffAreaFile->Extension->Filter; if (f->SnapshotsPresent && !f->DestroyAllSnapshotsPending) { e = diffAreaFile->Extension; df = diffAreaFile->Filter; ObReferenceObject(f->DeviceObject); ObReferenceObject(e->DeviceObject); ObReferenceObject(df->DeviceObject); break; } } KeReleaseSpinLock(&filter->SpinLock, irql); if (l == &filter->DiffAreaFilesOnThisFilter) { break; } if (VspDestroyAllSnapshots(f, NULL, FALSE, FALSE)) { VspLogError(e, df, VS_ABORT_SNAPSHOTS_DISMOUNT, STATUS_SUCCESS, 2, FALSE); VspDeleteControlItemsWithGuid(f, NULL, TRUE); } ObDereferenceObject(f->DeviceObject); ObDereferenceObject(e->DeviceObject); ObDereferenceObject(df->DeviceObject); } if (filter->SnapshotsPresent && !filter->DestroyAllSnapshotsPending) { KeAcquireSpinLock(&filter->SpinLock, &irql); if (IsListEmpty(&filter->VolumeList)) { KeReleaseSpinLock(&filter->SpinLock, irql); } else { e = CONTAINING_RECORD(filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); ObReferenceObject(e->DeviceObject); KeReleaseSpinLock(&filter->SpinLock, irql); if (VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE)) { VspLogError(e, e->Filter, VS_ABORT_SNAPSHOTS_DISMOUNT_ORIGINAL, STATUS_SUCCESS, 1, FALSE); VspDeleteControlItemsWithGuid(filter, NULL, TRUE); } ObDereferenceObject(e->DeviceObject); } } KeAcquireSpinLock(&filter->SpinLock, &irql); bitmap = (PRTL_BITMAP) InterlockedExchangePointer( (PVOID*) &filter->ProtectedBlocksBitmap, NULL); if (bitmap) { ExFreePool(bitmap->Buffer); ExFreePool(bitmap); } KeReleaseSpinLock(&filter->SpinLock, irql); VspReleaseNonPagedResource(filter); VspDecrementRefCount(filter); VolSnapWrite(filter->DeviceObject, irp); } VOID VspCreateStartBlockWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; PIRP irp = context->Filter.Irp; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); VspCreateStartBlock(filter, filter->FirstControlBlockVolumeOffset, filter->MaximumVolumeSpace); VspReleaseNonPagedResource(filter); VspDecrementRefCount(filter); IoCompleteRequest(irp, IO_DISK_INCREMENT); } NTSTATUS VspWriteContextCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID WriteContext ) { PVSP_WRITE_CONTEXT writeContext = (PVSP_WRITE_CONTEXT) WriteContext; PFILTER_EXTENSION filter = writeContext->Filter; PVOLUME_EXTENSION extension = writeContext->Extension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; PVSP_CONTEXT context; ASSERT(Irp == writeContext->Irp); KeAcquireSpinLock(&extension->SpinLock, &irql); RemoveEntryList(&writeContext->ListEntry); KeReleaseSpinLock(&extension->SpinLock, irql); while (!IsListEmpty(&writeContext->CompletionRoutines)) { l = RemoveHeadList(&writeContext->CompletionRoutines); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); workItem->WorkerRoutine(workItem->Parameter); ExFreePool(workItem); } VspFreeWriteContext(filter->Root, writeContext); if (!filter->ProtectedBlocksBitmap && irpSp->Parameters.Write.ByteOffset.QuadPart <= VSP_START_BLOCK_OFFSET && irpSp->Parameters.Write.ByteOffset.QuadPart + irpSp->Parameters.Write.Length > VSP_START_BLOCK_OFFSET) { context = VspAllocateContext(filter->Root); if (context) { context->Type = VSP_CONTEXT_TYPE_FILTER; ExInitializeWorkItem(&context->WorkItem, VspCreateStartBlockWorker, context); context->Filter.Filter = filter; context->Filter.Irp = Irp; VspAcquireNonPagedResource(filter, &context->WorkItem, TRUE); return STATUS_MORE_PROCESSING_REQUIRED; } } VspDecrementRefCount(filter); return STATUS_SUCCESS; } VOID VspCleanupDanglingSnapshots( IN PVOID Filter, IN NTSTATUS LogStatus, IN ULONG LogUniqueId ) { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) Filter; NTSTATUS status; VspLogError(NULL, filter, VS_ABORT_NO_DIFF_AREA_VOLUME, LogStatus, LogUniqueId, FALSE); ObReferenceObject(filter->DeviceObject); ObReferenceObject(filter->TargetObject); VspDeleteControlItemsWithGuid(filter, NULL, FALSE); InterlockedExchange(&filter->SnapshotDiscoveryPending, FALSE); KeSetEvent(&filter->EndCommitProcessCompleted, IO_NO_INCREMENT, FALSE); ASSERT(filter->FirstWriteProcessed); VspResumeVolumeIo(filter); KeWaitForSingleObject(&filter->Root->PastBootReinit, Executive, KernelMode, FALSE, NULL); status = VspOpenControlBlockFile(filter); if (!NT_SUCCESS(status)) { VspAcquireNonPagedResource(filter, NULL, FALSE); VspCreateStartBlock(filter, 0, filter->MaximumVolumeSpace); filter->FirstControlBlockVolumeOffset = 0; VspReleaseNonPagedResource(filter); } ObDereferenceObject(filter->TargetObject); ObDereferenceObject(filter->DeviceObject); } VOID VspPnpCleanupDangling( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->PnpWaitTimer.Filter; KIRQL irql; PLIST_ENTRY l; PVSP_COPY_ON_WRITE copyOnWrite; ASSERT(context->Type == VSP_CONTEXT_TYPE_PNP_WAIT_TIMER); VspFreeContext(filter->Root, context); VspPauseVolumeIo(filter); if (filter->SnapshotDiscoveryPending) { VspCleanupDanglingSnapshots(filter, STATUS_SUCCESS, 1); } else { VspResumeVolumeIo(filter); } KeAcquireSpinLock(&filter->SpinLock, &irql); while (!IsListEmpty(&filter->CopyOnWriteList)) { l = RemoveHeadList(&filter->CopyOnWriteList); copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); } KeReleaseSpinLock(&filter->SpinLock, irql); ObDereferenceObject(filter->DeviceObject); } VOID VspPnpTimerDpc( IN PKDPC TimerDpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->PnpWaitTimer.Filter; KIRQL irql; PLIST_ENTRY l; PVSP_COPY_ON_WRITE copyOnWrite; ASSERT(context->Type == VSP_CONTEXT_TYPE_PNP_WAIT_TIMER); ExFreePool(context->PnpWaitTimer.Timer); KeAcquireSpinLock(&filter->SpinLock, &irql); filter->PnpWaitTimerContext = NULL; KeReleaseSpinLock(&filter->SpinLock, irql); if (!filter->SnapshotDiscoveryPending) { KeAcquireSpinLock(&filter->SpinLock, &irql); while (!IsListEmpty(&filter->CopyOnWriteList)) { l = RemoveHeadList(&filter->CopyOnWriteList); copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); } KeReleaseSpinLock(&filter->SpinLock, irql); VspFreeContext(filter->Root, context); ObDereferenceObject(filter->DeviceObject); return; } ExInitializeWorkItem(&context->WorkItem, VspPnpCleanupDangling, context); ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue); } VOID VspStartCopyOnWriteCache( IN PVOID Filter ) { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) Filter; PVSP_LOOKUP_TABLE_ENTRY lookupEntry; NTSTATUS status; CHAR controlItemBuffer[VSP_BYTES_PER_CONTROL_ITEM]; PVSP_CONTROL_ITEM_SNAPSHOT snapshotControlItem = (PVSP_CONTROL_ITEM_SNAPSHOT) controlItemBuffer; PVSP_CONTEXT context; LARGE_INTEGER timeout; KIRQL irql; ASSERT(!IsListEmpty(&filter->SnapshotLookupTableEntries)); lookupEntry = CONTAINING_RECORD(filter->SnapshotLookupTableEntries.Blink, VSP_LOOKUP_TABLE_ENTRY, SnapshotFilterListEntry); status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &lookupEntry->SnapshotGuid, FALSE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { VspCleanupDanglingSnapshots(Filter, status, 2); ObDereferenceObject(filter->DeviceObject); return; } if (snapshotControlItem->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_DETECTION) { VspCleanupDanglingSnapshots(Filter, STATUS_SUCCESS, 3); ObDereferenceObject(filter->DeviceObject); return; } snapshotControlItem->SnapshotControlItemFlags |= VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_DETECTION; status = VspIoControlItem(filter, VSP_CONTROL_ITEM_TYPE_SNAPSHOT, &lookupEntry->SnapshotGuid, TRUE, controlItemBuffer, TRUE); if (!NT_SUCCESS(status)) { VspCleanupDanglingSnapshots(Filter, status, 4); ObDereferenceObject(filter->DeviceObject); return; } ASSERT(lookupEntry->SnapshotControlItemFlags& VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_DETECTION); lookupEntry->SnapshotControlItemFlags &= ~VSP_SNAPSHOT_CONTROL_ITEM_FLAG_DIRTY_DETECTION; context = VspAllocateContext(filter->Root); if (!context) { VspCleanupDanglingSnapshots(Filter, STATUS_INSUFFICIENT_RESOURCES, 5); ObDereferenceObject(filter->DeviceObject); return; } context->Type = VSP_CONTEXT_TYPE_PNP_WAIT_TIMER; context->PnpWaitTimer.Filter = filter; KeInitializeDpc(&context->PnpWaitTimer.TimerDpc, VspPnpTimerDpc, context); context->PnpWaitTimer.Timer = (PKTIMER) ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), VOLSNAP_TAG_SHORT_TERM); if (!context->PnpWaitTimer.Timer) { VspFreeContext(filter->Root, context); VspCleanupDanglingSnapshots(Filter, STATUS_INSUFFICIENT_RESOURCES, 6); ObDereferenceObject(filter->DeviceObject); return; } KeInitializeTimer(context->PnpWaitTimer.Timer); ObReferenceObject(filter->DeviceObject); KeAcquireSpinLock(&filter->SpinLock, &irql); filter->PnpWaitTimerContext = context; KeReleaseSpinLock(&filter->SpinLock, irql); timeout.QuadPart = (LONGLONG) -10*1000*1000*30; // 30 seconds. KeSetTimer(context->PnpWaitTimer.Timer, timeout, &context->PnpWaitTimer.TimerDpc); VspResumeVolumeIo(filter); ObDereferenceObject(filter->DeviceObject); } VOID VspAbortCopyOnWrites( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { KIRQL irql; KeAcquireSpinLock(&Filter->SpinLock, &irql); if (Filter->PnpWaitTimerContext && KeCancelTimer(Filter->PnpWaitTimerContext->PnpWaitTimer.Timer)) { KeReleaseSpinLock(&Filter->SpinLock, irql); VspPnpTimerDpc(NULL, Filter->PnpWaitTimerContext, NULL, NULL); } else { KeReleaseSpinLock(&Filter->SpinLock, irql); } if (!Irp->MdlAddress) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); VspDecrementRefCount(Filter); return; } IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspRefCountCompletionRoutine, Filter, TRUE, TRUE, TRUE); IoCallDriver(Filter->TargetObject, Irp); } NTSTATUS VspCopyOnWriteReadCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->CopyOnWrite.Filter; PIRP irp = context->CopyOnWrite.Irp; NTSTATUS status = Irp->IoStatus.Status; PVSP_COPY_ON_WRITE copyOnWrite, cow; KIRQL irql; PLIST_ENTRY l; ASSERT(context->Type == VSP_CONTEXT_TYPE_COPY_ON_WRITE); if (!NT_SUCCESS(status)) { VspFreeContext(filter->Root, context); ExFreePool(MmGetMdlVirtualAddress(Irp->MdlAddress)); IoFreeMdl(Irp->MdlAddress); IoFreeIrp(Irp); VspAbortCopyOnWrites(filter, irp); return STATUS_MORE_PROCESSING_REQUIRED; } copyOnWrite = (PVSP_COPY_ON_WRITE) ExAllocatePoolWithTag(NonPagedPool, sizeof(VSP_COPY_ON_WRITE), VOLSNAP_TAG_COPY); if (!copyOnWrite) { VspFreeContext(filter->Root, context); ExFreePool(MmGetMdlVirtualAddress(Irp->MdlAddress)); IoFreeMdl(Irp->MdlAddress); IoFreeIrp(Irp); VspAbortCopyOnWrites(filter, irp); return STATUS_MORE_PROCESSING_REQUIRED; } copyOnWrite->RoundedStart = context->CopyOnWrite.RoundedStart; copyOnWrite->Buffer = MmGetMdlVirtualAddress(Irp->MdlAddress); IoFreeMdl(Irp->MdlAddress); IoFreeIrp(Irp); KeAcquireSpinLock(&filter->SpinLock, &irql); for (l = filter->CopyOnWriteList.Flink; l != &filter->CopyOnWriteList; l = l->Flink) { cow = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); if (copyOnWrite->RoundedStart == cow->RoundedStart) { break; } } if (l == &filter->CopyOnWriteList) { InsertTailList(&filter->CopyOnWriteList, ©OnWrite->ListEntry); } else { ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); } KeReleaseSpinLock(&filter->SpinLock, irql); context->CopyOnWrite.RoundedStart += BLOCK_SIZE; VspStartCopyOnWrite(context); return STATUS_MORE_PROCESSING_REQUIRED; } VOID VspStartCopyOnWrite( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->CopyOnWrite.Filter; LONGLONG roundedStart = context->CopyOnWrite.RoundedStart; LONGLONG roundedEnd = context->CopyOnWrite.RoundedEnd; KIRQL irql; PLIST_ENTRY l; PVSP_COPY_ON_WRITE copyOnWrite; PIRP irp; PMDL mdl; PVOID buffer; PIO_STACK_LOCATION nextSp; ASSERT(context->Type == VSP_CONTEXT_TYPE_COPY_ON_WRITE); for (; roundedStart < roundedEnd; roundedStart += BLOCK_SIZE) { KeAcquireSpinLock(&filter->SpinLock, &irql); for (l = filter->CopyOnWriteList.Flink; l != &filter->CopyOnWriteList; l = l->Flink) { copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); if (copyOnWrite->RoundedStart == roundedStart) { break; } } KeReleaseSpinLock(&filter->SpinLock, irql); if (l != &filter->CopyOnWriteList) { continue; } irp = IoAllocateIrp(filter->TargetObject->StackSize, FALSE); buffer = ExAllocatePoolWithTag(NonPagedPool, BLOCK_SIZE, VOLSNAP_TAG_BUFFER); mdl = IoAllocateMdl(buffer, BLOCK_SIZE, FALSE, FALSE, NULL); if (!irp || !buffer || !mdl) { if (irp) { IoFreeIrp(irp); } if (mdl) { IoFreeMdl(mdl); } if (buffer) { ExFreePool(buffer); } irp = context->CopyOnWrite.Irp; VspFreeContext(filter->Root, context); VspAbortCopyOnWrites(filter, irp); return; } context->CopyOnWrite.RoundedStart = roundedStart; MmBuildMdlForNonPagedPool(mdl); irp->MdlAddress = mdl; irp->Tail.Overlay.Thread = PsGetCurrentThread(); nextSp = IoGetNextIrpStackLocation(irp); nextSp->Parameters.Read.ByteOffset.QuadPart = roundedStart; nextSp->Parameters.Read.Length = BLOCK_SIZE; nextSp->MajorFunction = IRP_MJ_READ; nextSp->DeviceObject = filter->TargetObject; nextSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; IoSetCompletionRoutine(irp, VspCopyOnWriteReadCompletion, context, TRUE, TRUE, TRUE); IoCallDriver(nextSp->DeviceObject, irp); return; } irp = context->CopyOnWrite.Irp; VspFreeContext(filter->Root, context); if (!irp->MdlAddress) { irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); VspDecrementRefCount(filter); return; } IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine(irp, VspRefCountCompletionRoutine, filter, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, irp); } VOID VspCopyOnWriteToNonPagedPool( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PVSP_CONTEXT context; context = VspAllocateContext(Filter->Root); if (!context) { VspAbortCopyOnWrites(Filter, Irp); return; } context->Type = VSP_CONTEXT_TYPE_COPY_ON_WRITE; context->CopyOnWrite.Filter = Filter; context->CopyOnWrite.Irp = Irp; context->CopyOnWrite.RoundedStart = irpSp->Parameters.Write.ByteOffset.QuadPart&(~(BLOCK_SIZE - 1)); context->CopyOnWrite.RoundedEnd = (irpSp->Parameters.Write.ByteOffset.QuadPart + irpSp->Parameters.Write.Length + BLOCK_SIZE - 1)& (~(BLOCK_SIZE - 1)); VspStartCopyOnWrite(context); } #ifdef ALLOC_PRAGMA #pragma code_seg() #endif #if DBG VOID VspCheckDiffAreaFile( IN PVSP_DIFF_AREA_FILE DiffAreaFile ) { LONGLONG fileOffset; PLIST_ENTRY l; PDIFF_AREA_FILE_ALLOCATION diffAreaFileAllocation; _try { fileOffset = DiffAreaFile->NextAvailable; for (l = DiffAreaFile->UnusedAllocationList.Flink; l != &DiffAreaFile->UnusedAllocationList; l = l->Flink) { diffAreaFileAllocation = CONTAINING_RECORD(l, DIFF_AREA_FILE_ALLOCATION, ListEntry); if (diffAreaFileAllocation->NLength < 0) { fileOffset -= diffAreaFileAllocation->NLength; } else { fileOffset += diffAreaFileAllocation->NLength; } } ASSERT(fileOffset == DiffAreaFile->AllocatedFileSize); } _except (EXCEPTION_EXECUTE_HANDLER) { } } #endif VOID VspLowPriorityWorkItem( IN PVOID RootExtension ) { PDO_EXTENSION root = (PDO_EXTENSION) RootExtension; KIRQL irql; PLIST_ENTRY l; for (;;) { root->ActualLowPriorityWorkItem->WorkerRoutine( root->ActualLowPriorityWorkItem->Parameter); KeAcquireSpinLock(&root->ESpinLock, &irql); if (IsListEmpty(&root->LowPriorityQueue)) { root->WorkerItemInUse = FALSE; KeReleaseSpinLock(&root->ESpinLock, irql); return; } l = RemoveHeadList(&root->LowPriorityQueue); KeReleaseSpinLock(&root->ESpinLock, irql); root->ActualLowPriorityWorkItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); } } VOID VspQueueLowPriorityWorkItem( IN PDO_EXTENSION RootExtension, IN PWORK_QUEUE_ITEM WorkItem ) { KIRQL irql; KeAcquireSpinLock(&RootExtension->ESpinLock, &irql); if (RootExtension->WorkerItemInUse) { InsertTailList(&RootExtension->LowPriorityQueue, &WorkItem->List); KeReleaseSpinLock(&RootExtension->ESpinLock, irql); return; } RootExtension->WorkerItemInUse = TRUE; KeReleaseSpinLock(&RootExtension->ESpinLock, irql); RootExtension->ActualLowPriorityWorkItem = WorkItem; ExInitializeWorkItem(&RootExtension->LowPriorityWorkItem, VspLowPriorityWorkItem, RootExtension); ExQueueWorkItem(&RootExtension->LowPriorityWorkItem, DelayedWorkQueue); } VOID VspCleanupPreamble( IN PFILTER_EXTENSION Filter ) { KIRQL irql; if (!Filter->IsOnline) { return; } Filter->IsOnline = FALSE; KeAcquireSpinLock(&Filter->SpinLock, &irql); if (Filter->SnapshotDiscoveryPending && !Filter->ActivateStarted && !Filter->FirstWriteProcessed) { InterlockedExchange(&Filter->SnapshotDiscoveryPending, FALSE); InterlockedExchange(&Filter->FirstWriteProcessed, TRUE); KeReleaseSpinLock(&Filter->SpinLock, irql); } else { KeReleaseSpinLock(&Filter->SpinLock, irql); VspPauseVolumeIo(Filter); } VspResumeVolumeIo(Filter); } VOID VspOfflineWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; PIRP irp = context->Filter.Irp; KEVENT event; KIRQL irql; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); VspFreeContext(filter->Root, context); KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine(irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); if (!NT_SUCCESS(irp->IoStatus.Status)) { VspRelease(filter->Root); VspReleaseCritical(filter); IoCompleteRequest(irp, IO_NO_INCREMENT); return; } VspCleanupPreamble(filter); VspRelease(filter->Root); VspCleanupFilter(filter, TRUE, FALSE); VspReleaseCritical(filter); IoCompleteRequest(irp, IO_NO_INCREMENT); } VOID VspStartDiffAreaFileCleanupTimer( IN PFILTER_EXTENSION Filter ) { PKTIMER timer; PKDPC dpc; PVSP_CONTEXT context; LARGE_INTEGER timeout; timer = (PKTIMER) ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), VOLSNAP_TAG_SHORT_TERM); if (!timer) { return; } dpc = (PKDPC) ExAllocatePoolWithTag(NonPagedPool, sizeof(KDPC), VOLSNAP_TAG_SHORT_TERM); if (!dpc) { ExFreePool(timer); return; } context = VspAllocateContext(Filter->Root); if (!context) { ExFreePool(dpc); ExFreePool(timer); return; } context->Type = VSP_CONTEXT_TYPE_DELETE_DA_FILES; context->DeleteDiffAreaFiles.Filter = Filter; context->DeleteDiffAreaFiles.Timer = timer; context->DeleteDiffAreaFiles.Dpc = dpc; ObReferenceObject(Filter->DeviceObject); KeInitializeTimer(timer); KeInitializeDpc(dpc, VspDeleteDiffAreaFilesTimerDpc, context); Filter->DeleteTimer = timer; timeout.QuadPart = (LONGLONG) 21*60*(-10*1000*1000); // 21 minutes. KeSetTimer(timer, timeout, dpc); } BOOLEAN FtpSupportsOnlineOffline( IN PFILTER_EXTENSION Filter ) { KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_SUPPORTS_ONLINE_OFFLINE, Filter->TargetObject, NULL, 0, NULL, 0, FALSE, &event, &ioStatus); if (!irp) { return FALSE; } status = IoCallDriver(Filter->TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return FALSE; } return TRUE; } VOID VspOnlineWorker( IN PVOID Context ) { PVSP_CONTEXT context = (PVSP_CONTEXT) Context; PFILTER_EXTENSION filter = context->Filter.Filter; PIRP irp = context->Filter.Irp; KEVENT event; BOOLEAN noControlItems; KIRQL irql; ASSERT(context->Type == VSP_CONTEXT_TYPE_FILTER); VspFreeContext(filter->Root, context); filter->IsOnline = TRUE; filter->IsRemoved = FALSE; VspPauseVolumeIo(filter); KeAcquireSpinLock(&filter->SpinLock, &irql); InterlockedExchange(&filter->FirstWriteProcessed, FALSE); filter->ActivateStarted = FALSE; KeReleaseSpinLock(&filter->SpinLock, irql); KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine(irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); if (!NT_SUCCESS(irp->IoStatus.Status)) { if (FtpSupportsOnlineOffline(filter)) { filter->IsOnline = FALSE; VspResumeVolumeIo(filter); VspRelease(filter->Root); VspReleaseCritical(filter); IoCompleteRequest(irp, IO_NO_INCREMENT); return; } } VspDiscoverSnapshots(filter, &noControlItems); if (!filter->SnapshotDiscoveryPending) { if (filter->FirstControlBlockVolumeOffset && !filter->SnapshotsPresent && !noControlItems) { VspLaunchOpenControlBlockFileWorker(filter); } } VspRelease(filter->Root); VspStartDiffAreaFileCleanupTimer(filter); VspReleaseCritical(filter); IoCompleteRequest(irp, IO_NO_INCREMENT); } NTSTATUS VspAbortPreparedSnapshot( IN PFILTER_EXTENSION Filter, IN BOOLEAN NeedLock, IN BOOLEAN IsFinalRemove ) /*++ Routine Description: This routine aborts the prepared snapshot. Arguments: Filter - Supplies the filter extension. Return Value: NTSTATUS --*/ { KIRQL irql; PVOLUME_EXTENSION extension; if (NeedLock) { VspAcquire(Filter->Root); } KeAcquireSpinLock(&Filter->SpinLock, &irql); extension = Filter->PreparedSnapshot; Filter->PreparedSnapshot = NULL; KeReleaseSpinLock(&Filter->SpinLock, irql); if (NeedLock) { VspRelease(Filter->Root); } if (!extension) { return STATUS_INVALID_PARAMETER; } VspCleanupInitialSnapshot(extension, NeedLock, IsFinalRemove); return STATUS_SUCCESS; } VOID VspAcquireNonPagedResource( IN PDEVICE_EXTENSION Extension, IN PWORK_QUEUE_ITEM WorkItem, IN BOOLEAN AlwaysPost ) { PFILTER_EXTENSION filter; KIRQL irql; VSP_CONTEXT context; BOOLEAN synchronousCall; if (Extension->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { filter = (PFILTER_EXTENSION) Extension; } else { filter = ((PVOLUME_EXTENSION) Extension)->Filter; } if (WorkItem) { synchronousCall = FALSE; } else { WorkItem = &context.WorkItem; context.Type = VSP_CONTEXT_TYPE_EVENT; KeInitializeEvent(&context.Event.Event, NotificationEvent, FALSE); ExInitializeWorkItem(&context.WorkItem, VspSignalContext, &context); synchronousCall = TRUE; } KeAcquireSpinLock(&filter->SpinLock, &irql); if (filter->NonPagedResourceInUse) { InsertTailList(&filter->NonPagedResourceList, &WorkItem->List); KeReleaseSpinLock(&filter->SpinLock, irql); if (synchronousCall) { KeWaitForSingleObject(&context.Event.Event, Executive, KernelMode, FALSE, NULL); } return; } filter->NonPagedResourceInUse = TRUE; KeReleaseSpinLock(&filter->SpinLock, irql); if (!synchronousCall) { if (AlwaysPost) { VspQueueWorkItem(filter->Root, WorkItem, 2); } else { WorkItem->WorkerRoutine(WorkItem->Parameter); } } } VOID VspReleaseNonPagedResource( IN PDEVICE_EXTENSION Extension ) { PFILTER_EXTENSION filter; KIRQL irql; PLIST_ENTRY l; PWORK_QUEUE_ITEM workItem; PVOLUME_EXTENSION extension; if (Extension->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { filter = (PFILTER_EXTENSION) Extension; } else { filter = ((PVOLUME_EXTENSION) Extension)->Filter; } KeAcquireSpinLock(&filter->SpinLock, &irql); #if DBG if (!IsListEmpty(&filter->VolumeList)) { extension = CONTAINING_RECORD(filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); if (extension->DiffAreaFile) { VspCheckDiffAreaFile(extension->DiffAreaFile); } } #endif if (IsListEmpty(&filter->NonPagedResourceList)) { filter->NonPagedResourceInUse = FALSE; KeReleaseSpinLock(&filter->SpinLock, irql); return; } l = RemoveHeadList(&filter->NonPagedResourceList); KeReleaseSpinLock(&filter->SpinLock, irql); workItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List); if (workItem->WorkerRoutine == VspSignalContext) { workItem->WorkerRoutine(workItem->Parameter); } else { VspQueueWorkItem(Extension->Root, workItem, 2); } } VOID VspDeleteDiffAreaFilesTimerDpc( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PVSP_CONTEXT context = (PVSP_CONTEXT) DeferredContext; PFILTER_EXTENSION filter = context->DeleteDiffAreaFiles.Filter; ASSERT(context->Type == VSP_CONTEXT_TYPE_DELETE_DA_FILES); ExInitializeWorkItem(&context->WorkItem, VspDeleteDiffAreaFilesWorker, context); VspQueueLowPriorityWorkItem(filter->Root, &context->WorkItem); } VOID VspResumeVolumeIo( IN PFILTER_EXTENSION Filter ) { KIRQL irql; BOOLEAN emptyQueue; LIST_ENTRY q; KeAcquireSpinLock(&Filter->SpinLock, &irql); ASSERT(Filter->HoldIncomingWrites); InterlockedIncrement(&Filter->RefCount); InterlockedDecrement(&Filter->HoldIncomingWrites); if (Filter->HoldIncomingWrites) { KeReleaseSpinLock(&Filter->SpinLock, irql); VspDecrementRefCount(Filter); KeReleaseSemaphore(&Filter->ZeroRefSemaphore, IO_NO_INCREMENT, 1, FALSE); return; } if (IsListEmpty(&Filter->HoldQueue)) { emptyQueue = FALSE; } else { emptyQueue = TRUE; q = Filter->HoldQueue; InitializeListHead(&Filter->HoldQueue); } KeResetEvent(&Filter->ZeroRefEvent); KeReleaseSpinLock(&Filter->SpinLock, irql); KeReleaseSemaphore(&Filter->ZeroRefSemaphore, IO_NO_INCREMENT, 1, FALSE); if (emptyQueue) { q.Blink->Flink = &q; q.Flink->Blink = &q; VspEmptyIrpQueue(Filter->Root->DriverObject, &q); } } VOID VspPauseVolumeIo( IN PFILTER_EXTENSION Filter ) { KIRQL irql; KeWaitForSingleObject(&Filter->ZeroRefSemaphore, Executive, KernelMode, FALSE, NULL); KeAcquireSpinLock(&Filter->SpinLock, &irql); if (Filter->HoldIncomingWrites) { InterlockedIncrement(&Filter->HoldIncomingWrites); KeReleaseSpinLock(&Filter->SpinLock, irql); } else { InterlockedIncrement(&Filter->HoldIncomingWrites); KeReleaseSpinLock(&Filter->SpinLock, irql); VspDecrementRefCount(Filter); } KeWaitForSingleObject(&Filter->ZeroRefEvent, Executive, KernelMode, FALSE, NULL); } NTSTATUS VspSignalCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Event ) { KeSetEvent((PKEVENT) Event, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS VspReleaseWrites( IN PFILTER_EXTENSION Filter ) /*++ Routine Description: This routine releases previously queued writes. If the writes have already been dequeued by a timeout of have never actually been queued for some other reason then this routine fails. Arguments: Filter - Supplies the filter extension. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { KIRQL irql; LONG r; LIST_ENTRY q; PLIST_ENTRY l; PIRP irp; BOOLEAN emptyQueue; if (!KeCancelTimer(&Filter->HoldWritesTimer)) { r = InterlockedExchange(&Filter->LastReleaseDueToMemoryPressure, FALSE); return r ? STATUS_INSUFFICIENT_RESOURCES : STATUS_INVALID_PARAMETER; } VspIrpsTimerDpc(NULL, Filter, NULL, NULL); return STATUS_SUCCESS; } VOID VspDecrementRefCount( IN PFILTER_EXTENSION Filter ) { KIRQL irql; ZERO_REF_CALLBACK callback; if (InterlockedDecrement(&Filter->RefCount)) { return; } KeAcquireSpinLock(&Filter->SpinLock, &irql); callback = Filter->ZeroRefCallback; Filter->ZeroRefCallback = NULL; KeReleaseSpinLock(&Filter->SpinLock, irql); if (callback) { callback(Filter); } KeSetEvent(&Filter->ZeroRefEvent, IO_NO_INCREMENT, FALSE); } VOID VspCleanupFilter( IN PFILTER_EXTENSION Filter, IN BOOLEAN IsOffline, IN BOOLEAN IsFinalRemove ) /*++ Routine Description: This routine cleans up filter extension data because of an IRP_MN_REMOVE. Arguments: Filter - Supplies the filter extension. Return Value: None. --*/ { KIRQL irql; PIRP irp; PRTL_BITMAP bitmap; PLIST_ENTRY l, ll; PVSP_DIFF_AREA_FILE diffAreaFile; PFILTER_EXTENSION f; LIST_ENTRY listOfDiffAreaFilesToClose; LIST_ENTRY listOfDeviceObjectsToDelete; HANDLE h, hh; FILE_DISPOSITION_INFORMATION dispInfo; IO_STATUS_BLOCK ioStatus; PVOLUME_EXTENSION extension; BOOLEAN b; Filter->DeleteTimer = NULL; IoAcquireCancelSpinLock(&irql); irp = Filter->FlushAndHoldIrp; if (irp) { irp->CancelIrql = irql; IoSetCancelRoutine(irp, NULL); VspCancelRoutine(Filter->DeviceObject, irp); } else { IoReleaseCancelSpinLock(irql); } VspReleaseWrites(Filter); VspAcquire(Filter->Root); Filter->IsRemoved = TRUE; KeAcquireSpinLock(&Filter->SpinLock, &irql); bitmap = (PRTL_BITMAP) InterlockedExchangePointer( (PVOID*) &Filter->ProtectedBlocksBitmap, NULL); if (bitmap) { ExFreePool(bitmap->Buffer); ExFreePool(bitmap); } KeReleaseSpinLock(&Filter->SpinLock, irql); VspRelease(Filter->Root); VspAbortPreparedSnapshot(Filter, TRUE, IsFinalRemove); InitializeListHead(&listOfDiffAreaFilesToClose); InitializeListHead(&listOfDeviceObjectsToDelete); VspAcquire(Filter->Root); b = FALSE; while (!IsListEmpty(&Filter->VolumeList)) { VspDeleteOldestSnapshot(Filter, &listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete, TRUE, TRUE); b = TRUE; } if (b && !IsFinalRemove) { IoInvalidateDeviceRelations(Filter->Pdo, BusRelations); } if (!IsOffline && !Filter->NotInFilterList) { RemoveEntryList(&Filter->ListEntry); Filter->NotInFilterList = TRUE; } if (!IsOffline) { Filter->DiffAreaVolume = NULL; for (l = Filter->Root->FilterList.Flink; l != &Filter->Root->FilterList; l = l->Flink) { f = CONTAINING_RECORD(l, FILTER_EXTENSION, ListEntry); if (f->DiffAreaVolume == Filter) { f->DiffAreaVolume = NULL; } } } while (!IsListEmpty(&Filter->DiffAreaFilesOnThisFilter)) { l = Filter->DiffAreaFilesOnThisFilter.Flink; diffAreaFile = CONTAINING_RECORD(l, VSP_DIFF_AREA_FILE, FilterListEntry); f = diffAreaFile->Extension->Filter; if (IsOffline) { VspLogError(diffAreaFile->Extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_OFFLINE, STATUS_SUCCESS, 0, TRUE); } else { VspLogError(diffAreaFile->Extension, diffAreaFile->Filter, VS_ABORT_SNAPSHOTS_DISMOUNT, STATUS_SUCCESS, 3, TRUE); } VspAbortPreparedSnapshot(f, FALSE, FALSE); b = FALSE; while (!IsListEmpty(&f->VolumeList)) { VspDeleteOldestSnapshot(f, &listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete, FALSE, TRUE); b = TRUE; } if (b) { IoInvalidateDeviceRelations(f->Pdo, BusRelations); } } h = InterlockedExchangePointer(&Filter->ControlBlockFileHandle, NULL); VspAcquireNonPagedResource(Filter, NULL, FALSE); Filter->FirstControlBlockVolumeOffset = 0; if (!IsOffline && Filter->SnapshotOnDiskIrp) { ExFreePool(MmGetMdlVirtualAddress( Filter->SnapshotOnDiskIrp->MdlAddress)); IoFreeMdl(Filter->SnapshotOnDiskIrp->MdlAddress); IoFreeIrp(Filter->SnapshotOnDiskIrp); Filter->SnapshotOnDiskIrp = NULL; } VspRemoveLookupEntries(Filter); VspReleaseNonPagedResource(Filter); hh = InterlockedExchangePointer(&Filter->BootStatHandle, NULL); if (!IsOffline) { while (!IsListEmpty(&Filter->DeadVolumeList)) { l = RemoveHeadList(&Filter->DeadVolumeList); extension = CONTAINING_RECORD(l, VOLUME_EXTENSION, ListEntry); InterlockedExchange(&extension->DeadToPnp, TRUE); } } VspRelease(Filter->Root); VspCloseDiffAreaFiles(&listOfDiffAreaFilesToClose, &listOfDeviceObjectsToDelete); if (h) { dispInfo.DeleteFile = FALSE; ZwSetInformationFile(h, &ioStatus, &dispInfo, sizeof(dispInfo), FileDispositionInformation); ZwClose(h); } if (hh) { ZwClose(hh); } } NTSTATUS VolSnapPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_POWER. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; PVOLUME_EXTENSION extension; if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(filter->TargetObject, Irp); } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); switch (irpSp->MinorFunction) { case IRP_MN_WAIT_WAKE: case IRP_MN_POWER_SEQUENCE: case IRP_MN_SET_POWER: status = STATUS_SUCCESS; break; case IRP_MN_QUERY_POWER: if (irpSp->Parameters.Power.ShutdownType == PowerActionHibernate) { extension = (PVOLUME_EXTENSION) filter; if (extension->IsPreExposure) { status = STATUS_SUCCESS; } else { status = VspHandleHibernateNotification(extension, Irp); } } else { status = STATUS_SUCCESS; } break; default: status = Irp->IoStatus.Status; break; } if (status == STATUS_PENDING) { return STATUS_PENDING; } Irp->IoStatus.Status = status; PoStartNextPowerIrp(Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS VolSnapRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_READ. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; NTSTATUS status; PVOLUME_EXTENSION extension; KIRQL irql; if (filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER) { IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(filter->TargetObject, Irp); } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME); extension = (PVOLUME_EXTENSION) filter; filter = extension->Filter; if (!extension->IsStarted || extension->IsPreExposure) { Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NO_SUCH_DEVICE; } if (extension->IsOffline) { Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_DEVICE_OFF_LINE; } status = VspIncrementVolumeRefCount(extension); if (!NT_SUCCESS(status)) { Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } IoMarkIrpPending(Irp); KeAcquireSpinLock(&extension->SpinLock, &irql); if (VspAreBitsSet(extension, Irp)) { KeReleaseSpinLock(&extension->SpinLock, irql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = IoGetCurrentIrpStackLocation(Irp)-> Parameters.Read.Length; VspReadCompletionForReadSnapshot(DeviceObject, Irp, extension); return STATUS_PENDING; } KeReleaseSpinLock(&extension->SpinLock, irql); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspReadCompletionForReadSnapshot, extension, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); return STATUS_PENDING; } NTSTATUS VspIncrementRefCount( IN PFILTER_EXTENSION Filter, IN PIRP Irp ) { KIRQL irql; InterlockedIncrement(&Filter->RefCount); if (!Filter->HoldIncomingWrites) { return STATUS_SUCCESS; } VspDecrementRefCount(Filter); KeAcquireSpinLock(&Filter->SpinLock, &irql); if (!Filter->HoldIncomingWrites) { InterlockedIncrement(&Filter->RefCount); KeReleaseSpinLock(&Filter->SpinLock, irql); return STATUS_SUCCESS; } IoMarkIrpPending(Irp); InsertTailList(&Filter->HoldQueue, &Irp->Tail.Overlay.ListEntry); KeReleaseSpinLock(&Filter->SpinLock, irql); return STATUS_PENDING; } NTSTATUS VspRefCountCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Filter ) { VspDecrementRefCount((PFILTER_EXTENSION) Filter); return STATUS_SUCCESS; } NTSTATUS VolSnapWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_WRITE. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; NTSTATUS status; LONG r; PVOLUME_EXTENSION extension; KIRQL irql, irql2; PLIST_ENTRY l; PVSP_DIFF_AREA_FILE diffAreaFile; PIO_STACK_LOCATION nextSp; PVSP_CONTEXT context; PDO_EXTENSION rootExtension; PVSP_WRITE_CONTEXT writeContext; PVSP_COPY_ON_WRITE copyOnWrite; if (filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER); IoMarkIrpPending(Irp); status = VspIncrementRefCount(filter, Irp); if (status == STATUS_PENDING) { if (filter->SnapshotDiscoveryPending) { KeAcquireSpinLock(&filter->SpinLock, &irql); if (filter->ActivateStarted) { KeReleaseSpinLock(&filter->SpinLock, irql); return STATUS_PENDING; } if (filter->FirstWriteProcessed) { KeReleaseSpinLock(&filter->SpinLock, irql); return STATUS_PENDING; } InterlockedExchange(&filter->FirstWriteProcessed, TRUE); while (!IsListEmpty(&filter->CopyOnWriteList)) { l = RemoveHeadList(&filter->CopyOnWriteList); copyOnWrite = CONTAINING_RECORD(l, VSP_COPY_ON_WRITE, ListEntry); ExFreePool(copyOnWrite->Buffer); ExFreePool(copyOnWrite); } KeReleaseSpinLock(&filter->SpinLock, irql); ObReferenceObject(filter->DeviceObject); ExInitializeWorkItem(&filter->DestroyContext.WorkItem, VspStartCopyOnWriteCache, filter); ExQueueWorkItem(&filter->DestroyContext.WorkItem, DelayedWorkQueue); } return STATUS_PENDING; } if (filter->ProtectedBlocksBitmap) { KeAcquireSpinLock(&filter->SpinLock, &irql); if (filter->ProtectedBlocksBitmap && !VspAreBitsClear(filter->ProtectedBlocksBitmap, Irp)) { KeReleaseSpinLock(&filter->SpinLock, irql); context = VspAllocateContext(filter->Root); if (!context) { rootExtension = filter->Root; KeAcquireSpinLock(&rootExtension->ESpinLock, &irql); if (rootExtension->EmergencyContextInUse) { InsertTailList(&rootExtension->IrpWaitingList, &Irp->Tail.Overlay.ListEntry); if (!rootExtension->IrpWaitingListNeedsChecking) { InterlockedExchange( &rootExtension->IrpWaitingListNeedsChecking, TRUE); } KeReleaseSpinLock(&rootExtension->ESpinLock, irql); VspDecrementRefCount(filter); return STATUS_PENDING; } rootExtension->EmergencyContextInUse = TRUE; KeReleaseSpinLock(&rootExtension->ESpinLock, irql); context = rootExtension->EmergencyContext; } context->Type = VSP_CONTEXT_TYPE_DISMOUNT_CLEANUP; ExInitializeWorkItem(&context->WorkItem, VspDismountCleanupOnWrite, context); context->DismountCleanupOnWrite.Filter = filter; context->DismountCleanupOnWrite.Irp = Irp; VspAcquireNonPagedResource(filter, &context->WorkItem, FALSE); return STATUS_PENDING; } KeReleaseSpinLock(&filter->SpinLock, irql); } if (filter->SnapshotDiscoveryPending) { VspCopyOnWriteToNonPagedPool(filter, Irp); return STATUS_PENDING; } if (!filter->SnapshotsPresent) { if (!Irp->MdlAddress) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); VspDecrementRefCount(filter); return STATUS_PENDING; } IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspRefCountCompletionRoutine, filter, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); return STATUS_PENDING; } extension = CONTAINING_RECORD(filter->VolumeList.Blink, VOLUME_EXTENSION, ListEntry); KeAcquireSpinLock(&extension->SpinLock, &irql); if (VspAreBitsSet(extension, Irp)) { if (!Irp->MdlAddress) { KeReleaseSpinLock(&extension->SpinLock, irql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); VspDecrementRefCount(filter); return STATUS_PENDING; } writeContext = VspAllocateWriteContext(filter->Root); if (!writeContext) { rootExtension = filter->Root; KeAcquireSpinLock(&rootExtension->ESpinLock, &irql2); if (rootExtension->EmergencyWriteContextInUse) { InsertTailList(&rootExtension->WriteContextIrpWaitingList, &Irp->Tail.Overlay.ListEntry); if (!rootExtension->WriteContextIrpWaitingListNeedsChecking) { InterlockedExchange( &rootExtension->WriteContextIrpWaitingListNeedsChecking, TRUE); } KeReleaseSpinLock(&rootExtension->ESpinLock, irql2); KeReleaseSpinLock(&extension->SpinLock, irql); VspDecrementRefCount(filter); return STATUS_PENDING; } rootExtension->EmergencyWriteContextInUse = TRUE; KeReleaseSpinLock(&rootExtension->ESpinLock, irql2); writeContext = rootExtension->EmergencyWriteContext; } writeContext->Filter = filter; writeContext->Extension = extension; writeContext->Irp = Irp; InitializeListHead(&writeContext->CompletionRoutines); InsertTailList(&extension->WriteContextList, &writeContext->ListEntry); KeReleaseSpinLock(&extension->SpinLock, irql); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspWriteContextCompletionRoutine, writeContext, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); return STATUS_PENDING; } KeReleaseSpinLock(&extension->SpinLock, irql); context = VspAllocateContext(extension->Root); if (!context) { rootExtension = extension->Root; KeAcquireSpinLock(&rootExtension->ESpinLock, &irql); if (rootExtension->EmergencyContextInUse) { InsertTailList(&rootExtension->IrpWaitingList, &Irp->Tail.Overlay.ListEntry); if (!rootExtension->IrpWaitingListNeedsChecking) { InterlockedExchange( &rootExtension->IrpWaitingListNeedsChecking, TRUE); } KeReleaseSpinLock(&rootExtension->ESpinLock, irql); VspDecrementRefCount(filter); return STATUS_PENDING; } rootExtension->EmergencyContextInUse = TRUE; KeReleaseSpinLock(&rootExtension->ESpinLock, irql); context = rootExtension->EmergencyContext; } ASSERT(extension->DiffAreaFile); diffAreaFile = extension->DiffAreaFile; status = VspIncrementRefCount(diffAreaFile->Filter, Irp); if (status == STATUS_PENDING) { VspFreeContext(extension->Root, context); VspDecrementRefCount(filter); return STATUS_PENDING; } nextSp = IoGetNextIrpStackLocation(Irp); nextSp->Parameters.Write.Length = 1; // Use this for a ref count. context->Type = VSP_CONTEXT_TYPE_WRITE_VOLUME; context->WriteVolume.Extension = extension; context->WriteVolume.Irp = Irp; context->WriteVolume.RoundedStart = 0; ExInitializeWorkItem(&context->WorkItem, VspWriteVolume, context); if (extension->OnDiskNotCommitted) { VspAcquireNonPagedResource(extension, &context->WorkItem, TRUE); } else { VspAcquireNonPagedResource(extension, &context->WorkItem, FALSE); } return STATUS_PENDING; } NTSTATUS VolSnapCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_CLEANUP. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PFILTER_EXTENSION filter = (PFILTER_EXTENSION) DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); KEVENT event; KIRQL irql; PIRP irp; PVSP_CONTEXT context; if (filter->DeviceExtensionType == DEVICE_EXTENSION_VOLUME) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } ASSERT(filter->DeviceExtensionType == DEVICE_EXTENSION_FILTER); KeInitializeEvent(&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, VspSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(filter->TargetObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); IoAcquireCancelSpinLock(&irql); if (filter->FlushAndHoldIrp) { irp = filter->FlushAndHoldIrp; if (IoGetCurrentIrpStackLocation(irp)->FileObject == irpSp->FileObject) { irp->CancelIrql = irql; IoSetCancelRoutine(irp, NULL); VspCancelRoutine(DeviceObject, irp); } else { IoReleaseCancelSpinLock(irql); } } else { IoReleaseCancelSpinLock(irql); } IoAcquireCancelSpinLock(&irql); if (filter->AutoCleanupFileObject == irpSp->FileObject) { filter->AutoCleanupFileObject = NULL; IoReleaseCancelSpinLock(irql); VspDestroyAllSnapshots(filter, NULL, FALSE, FALSE); } else { IoReleaseCancelSpinLock(irql); } Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } BOOLEAN VspIsSetup( ) { UNICODE_STRING keyName; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE h; ULONG zero, result; RTL_QUERY_REGISTRY_TABLE queryTable[2]; RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\setupdd"); InitializeObjectAttributes(&oa, &keyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenKey(&h, 0, &oa); if (NT_SUCCESS(status)) { ZwClose(h); return TRUE; } zero = 0; RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"SystemSetupInProgress"; queryTable[0].EntryContext = &result; queryTable[0].DefaultType = REG_DWORD; queryTable[0].DefaultData = &zero; queryTable[0].DefaultLength = sizeof(ULONG); status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\Machine\\SYSTEM\\Setup", queryTable, NULL, NULL); if (!NT_SUCCESS(status)) { result = zero; } return result ? TRUE : FALSE; } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is called when the driver loads loads. Arguments: DriverObject - Supplies the driver object. RegistryPath - Supplies the registry path. Return Value: NTSTATUS --*/ { ULONG i; PDEVICE_OBJECT deviceObject; NTSTATUS status; PDO_EXTENSION rootExtension; for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = VolSnapDefaultDispatch; } DriverObject->DriverExtension->AddDevice = VolSnapAddDevice; DriverObject->MajorFunction[IRP_MJ_CREATE] = VolSnapCreate; DriverObject->MajorFunction[IRP_MJ_READ] = VolSnapRead; DriverObject->MajorFunction[IRP_MJ_WRITE] = VolSnapWrite; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VolSnapDeviceControl; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = VolSnapCleanup; DriverObject->MajorFunction[IRP_MJ_PNP] = VolSnapPnp; DriverObject->MajorFunction[IRP_MJ_POWER] = VolSnapPower; status = IoAllocateDriverObjectExtension(DriverObject, VolSnapAddDevice, sizeof(DO_EXTENSION), (PVOID*) &rootExtension); if (!NT_SUCCESS(status)) { return status; } RtlZeroMemory(rootExtension, sizeof(DO_EXTENSION)); rootExtension->DriverObject = DriverObject; InitializeListHead(&rootExtension->FilterList); InitializeListHead(&rootExtension->HoldIrps); KeInitializeTimer(&rootExtension->HoldTimer); KeInitializeDpc(&rootExtension->HoldTimerDpc, VspFsTimerDpc, rootExtension); KeInitializeSemaphore(&rootExtension->Semaphore, 1, 1); for (i = 0; i < NUMBER_OF_THREAD_POOLS; i++) { InitializeListHead(&rootExtension->WorkerQueue[i]); KeInitializeSemaphore(&rootExtension->WorkerSemaphore[i], 0, MAXLONG); KeInitializeSpinLock(&rootExtension->SpinLock[i]); } KeInitializeSemaphore(&rootExtension->ThreadsRefCountSemaphore, 1, 1); InitializeListHead(&rootExtension->LowPriorityQueue); IoRegisterDriverReinitialization(DriverObject, VspDriverReinit, rootExtension); IoRegisterBootDriverReinitialization(DriverObject, VspDriverBootReinit, rootExtension); ExInitializeNPagedLookasideList(&rootExtension->ContextLookasideList, NULL, NULL, 0, sizeof(VSP_CONTEXT), VOLSNAP_TAG_CONTEXT, 32); rootExtension->EmergencyContext = VspAllocateContext(rootExtension); if (!rootExtension->EmergencyContext) { ExDeleteNPagedLookasideList(&rootExtension->ContextLookasideList); return STATUS_INSUFFICIENT_RESOURCES; } InitializeListHead(&rootExtension->IrpWaitingList); KeInitializeSpinLock(&rootExtension->ESpinLock); ExInitializeNPagedLookasideList( &rootExtension->WriteContextLookasideList, NULL, NULL, 0, sizeof(VSP_WRITE_CONTEXT), VOLSNAP_TAG_CONTEXT, 32); rootExtension->EmergencyWriteContext = VspAllocateWriteContext(rootExtension); if (!rootExtension->EmergencyWriteContext) { ExDeleteNPagedLookasideList(&rootExtension->ContextLookasideList); ExDeleteNPagedLookasideList(&rootExtension->WriteContextLookasideList); return STATUS_INSUFFICIENT_RESOURCES; } InitializeListHead(&rootExtension->WriteContextIrpWaitingList); ExInitializeNPagedLookasideList(&rootExtension->TempTableEntryLookasideList, NULL, NULL, 0, sizeof(RTL_BALANCED_LINKS) + sizeof(TEMP_TRANSLATION_TABLE_ENTRY), VOLSNAP_TAG_TEMP_TABLE, 32); rootExtension->EmergencyTableEntry = VspAllocateTempTableEntry(rootExtension); if (!rootExtension->EmergencyTableEntry) { ExFreeToNPagedLookasideList(&rootExtension->ContextLookasideList, rootExtension->EmergencyContext); ExDeleteNPagedLookasideList(&rootExtension->WriteContextLookasideList); ExDeleteNPagedLookasideList( &rootExtension->TempTableEntryLookasideList); ExDeleteNPagedLookasideList(&rootExtension->ContextLookasideList); return STATUS_INSUFFICIENT_RESOURCES; } InitializeListHead(&rootExtension->WorkItemWaitingList); rootExtension->RegistryPath.Length = RegistryPath->Length; rootExtension->RegistryPath.MaximumLength = rootExtension->RegistryPath.Length + sizeof(WCHAR); rootExtension->RegistryPath.Buffer = (PWSTR) ExAllocatePoolWithTag(PagedPool, rootExtension->RegistryPath.MaximumLength, VOLSNAP_TAG_SHORT_TERM); if (!rootExtension->RegistryPath.Buffer) { VspFreeTempTableEntry(rootExtension, rootExtension->EmergencyTableEntry); ExFreeToNPagedLookasideList(&rootExtension->ContextLookasideList, rootExtension->EmergencyContext); ExDeleteNPagedLookasideList(&rootExtension->WriteContextLookasideList); ExDeleteNPagedLookasideList( &rootExtension->TempTableEntryLookasideList); ExDeleteNPagedLookasideList(&rootExtension->ContextLookasideList); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(rootExtension->RegistryPath.Buffer, RegistryPath->Buffer, RegistryPath->Length); rootExtension->RegistryPath.Buffer[RegistryPath->Length/ sizeof(WCHAR)] = 0; InitializeListHead(&rootExtension->AdjustBitmapQueue); KeInitializeEvent(&rootExtension->PastBootReinit, NotificationEvent, FALSE); RtlInitializeGenericTable(&rootExtension->PersistentSnapshotLookupTable, VspSnapshotLookupCompareRoutine, VspSnapshotLookupAllocateRoutine, VspSnapshotLookupFreeRoutine, NULL); KeInitializeMutex(&rootExtension->LookupTableMutex, 0); rootExtension->IsSetup = VspIsSetup(); RtlInitializeGenericTable(&rootExtension->UsedDevnodeNumbers, VspUsedDevnodeCompareRoutine, VspSnapshotLookupAllocateRoutine, VspSnapshotLookupFreeRoutine, NULL); return STATUS_SUCCESS; }