/** * @file sys/device.c * * @copyright 2015-2016 Bill Zissimopoulos */ /* * This file is part of WinFsp. * * You can redistribute it and/or modify it under the terms of the GNU * General Public License version 3 as published by the Free Software * Foundation. * * Licensees holding a valid commercial license may use this file in * accordance with the commercial license agreement provided with the * software. */ #include NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize, PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics, PUNICODE_STRING DeviceSddl, LPCGUID DeviceClassGuid, PDEVICE_OBJECT *PDeviceObject); NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics, PDEVICE_OBJECT *PDeviceObject); NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject); VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject); BOOLEAN FspDeviceReference(PDEVICE_OBJECT DeviceObject); VOID FspDeviceDereference(PDEVICE_OBJECT DeviceObject); _IRQL_requires_(DISPATCH_LEVEL) static BOOLEAN FspDeviceReferenceAtDpcLevel(PDEVICE_OBJECT DeviceObject); _IRQL_requires_(DISPATCH_LEVEL) static VOID FspDeviceDereferenceFromDpcLevel(PDEVICE_OBJECT DeviceObject); static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject); static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject); static IO_TIMER_ROUTINE FspFsvolDeviceTimerRoutine; static WORKER_THREAD_ROUTINE FspFsvolDeviceExpirationRoutine; VOID FspFsvolDeviceFileRenameAcquireShared(PDEVICE_OBJECT DeviceObject); VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject); VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner); VOID FspFsvolDeviceFileRenameRelease(PDEVICE_OBJECT DeviceObject); VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner); VOID FspFsvolDeviceLockContextTable(PDEVICE_OBJECT DeviceObject); VOID FspFsvolDeviceUnlockContextTable(PDEVICE_OBJECT DeviceObject); NTSTATUS FspFsvolDeviceCopyContextByNameList(PDEVICE_OBJECT DeviceObject, PVOID **PContexts, PULONG PContextCount); VOID FspFsvolDeviceDeleteContextByNameList(PVOID *Contexts, ULONG ContextCount); PVOID FspFsvolDeviceEnumerateContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName, BOOLEAN SubpathOnly, PVOID *PRestartKey); PVOID FspFsvolDeviceLookupContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName); PVOID FspFsvolDeviceInsertContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName, PVOID Context, FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT *ElementStorage, PBOOLEAN PInserted); VOID FspFsvolDeviceDeleteContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName, PBOOLEAN PDeleted); static RTL_AVL_COMPARE_ROUTINE FspFsvolDeviceCompareContextByName; static RTL_AVL_ALLOCATE_ROUTINE FspFsvolDeviceAllocateContextByName; static RTL_AVL_FREE_ROUTINE FspFsvolDeviceFreeContextByName; VOID FspFsvolDeviceGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_INFO *VolumeInfo); BOOLEAN FspFsvolDeviceTryGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_INFO *VolumeInfo); VOID FspFsvolDeviceSetVolumeInfo(PDEVICE_OBJECT DeviceObject, const FSP_FSCTL_VOLUME_INFO *VolumeInfo); VOID FspFsvolDeviceInvalidateVolumeInfo(PDEVICE_OBJECT DeviceObject); NTSTATUS FspDeviceCopyList( PDEVICE_OBJECT **PDeviceObjects, PULONG PDeviceObjectCount); VOID FspDeviceDeleteList( PDEVICE_OBJECT *DeviceObjects, ULONG DeviceObjectCount); VOID FspDeviceDeleteAll(VOID); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspDeviceCreateSecure) #pragma alloc_text(PAGE, FspDeviceCreate) #pragma alloc_text(PAGE, FspDeviceInitialize) #pragma alloc_text(PAGE, FspDeviceDelete) #pragma alloc_text(PAGE, FspFsvolDeviceInit) #pragma alloc_text(PAGE, FspFsvolDeviceFini) #pragma alloc_text(PAGE, FspFsvolDeviceFileRenameAcquireShared) #pragma alloc_text(PAGE, FspFsvolDeviceFileRenameAcquireExclusive) #pragma alloc_text(PAGE, FspFsvolDeviceFileRenameSetOwner) #pragma alloc_text(PAGE, FspFsvolDeviceFileRenameRelease) #pragma alloc_text(PAGE, FspFsvolDeviceFileRenameReleaseOwner) #pragma alloc_text(PAGE, FspFsvolDeviceLockContextTable) #pragma alloc_text(PAGE, FspFsvolDeviceUnlockContextTable) #pragma alloc_text(PAGE, FspFsvolDeviceCopyContextByNameList) #pragma alloc_text(PAGE, FspFsvolDeviceDeleteContextByNameList) #pragma alloc_text(PAGE, FspFsvolDeviceEnumerateContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceLookupContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceInsertContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceDeleteContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceCompareContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceAllocateContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceFreeContextByName) #pragma alloc_text(PAGE, FspDeviceCopyList) #pragma alloc_text(PAGE, FspDeviceDeleteList) #pragma alloc_text(PAGE, FspDeviceDeleteAll) #endif NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize, PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics, PUNICODE_STRING DeviceSddl, LPCGUID DeviceClassGuid, PDEVICE_OBJECT *PDeviceObject) { PAGED_CODE(); NTSTATUS Result; ULONG DeviceExtensionSize; PDEVICE_OBJECT DeviceObject; FSP_DEVICE_EXTENSION *DeviceExtension; *PDeviceObject = 0; switch (Kind) { case FspFsvolDeviceExtensionKind: DeviceExtensionSize = sizeof(FSP_FSVOL_DEVICE_EXTENSION); break; case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: DeviceExtensionSize = sizeof(FSP_DEVICE_EXTENSION); break; default: ASSERT(0); return STATUS_INVALID_PARAMETER; } if (0 != DeviceSddl) Result = IoCreateDeviceSecure(FspDriverObject, DeviceExtensionSize + ExtraSize, DeviceName, DeviceType, DeviceCharacteristics, FALSE, DeviceSddl, DeviceClassGuid, &DeviceObject); else Result = IoCreateDevice(FspDriverObject, DeviceExtensionSize + ExtraSize, DeviceName, DeviceType, DeviceCharacteristics, FALSE, &DeviceObject); if (!NT_SUCCESS(Result)) return Result; DeviceExtension = FspDeviceExtension(DeviceObject); KeInitializeSpinLock(&DeviceExtension->SpinLock); DeviceExtension->RefCount = 1; DeviceExtension->Kind = Kind; *PDeviceObject = DeviceObject; return Result; } NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics, PDEVICE_OBJECT *PDeviceObject) { PAGED_CODE(); return FspDeviceCreateSecure(Kind, ExtraSize, 0, DeviceType, DeviceCharacteristics, 0, 0, PDeviceObject); } NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); NTSTATUS Result; FSP_DEVICE_EXTENSION *DeviceExtension = FspDeviceExtension(DeviceObject); switch (DeviceExtension->Kind) { case FspFsvolDeviceExtensionKind: Result = FspFsvolDeviceInit(DeviceObject); break; case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: Result = STATUS_SUCCESS; break; default: ASSERT(0); return STATUS_INVALID_PARAMETER; } if (NT_SUCCESS(Result)) ClearFlag(DeviceObject->Flags, DO_DEVICE_INITIALIZING); return Result; } VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_DEVICE_EXTENSION *DeviceExtension = FspDeviceExtension(DeviceObject); switch (DeviceExtension->Kind) { case FspFsvolDeviceExtensionKind: FspFsvolDeviceFini(DeviceObject); break; case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: break; default: ASSERT(0); return; } #if DBG #pragma prefast(suppress:28175, "Debugging only: ok to access DeviceObject->Size") RtlFillMemory(&DeviceExtension->Kind, (PUINT8)DeviceObject + DeviceObject->Size - (PUINT8)&DeviceExtension->Kind, 0xBD); #endif IoDeleteDevice(DeviceObject); } BOOLEAN FspDeviceReference(PDEVICE_OBJECT DeviceObject) { // !PAGED_CODE(); BOOLEAN Result; FSP_DEVICE_EXTENSION *DeviceExtension; KIRQL Irql; DeviceExtension = FspDeviceExtension(DeviceObject); KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); Result = 0 != DeviceExtension->RefCount; if (Result) DeviceExtension->RefCount++; KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); return Result; } VOID FspDeviceDereference(PDEVICE_OBJECT DeviceObject) { // !PAGED_CODE(); BOOLEAN Delete = FALSE; FSP_DEVICE_EXTENSION *DeviceExtension; KIRQL Irql; DeviceExtension = FspDeviceExtension(DeviceObject); KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); if (0 != DeviceExtension->RefCount) { DeviceExtension->RefCount--; Delete = 0 == DeviceExtension->RefCount; } KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); if (Delete) FspDeviceDelete(DeviceObject); } _IRQL_requires_(DISPATCH_LEVEL) static BOOLEAN FspDeviceReferenceAtDpcLevel(PDEVICE_OBJECT DeviceObject) { // !PAGED_CODE(); BOOLEAN Result; FSP_DEVICE_EXTENSION *DeviceExtension; DeviceExtension = FspDeviceExtension(DeviceObject); KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); Result = 0 != DeviceExtension->RefCount; if (Result) DeviceExtension->RefCount++; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return Result; } _IRQL_requires_(DISPATCH_LEVEL) static VOID FspDeviceDereferenceFromDpcLevel(PDEVICE_OBJECT DeviceObject) { // !PAGED_CODE(); BOOLEAN Delete = FALSE; FSP_DEVICE_EXTENSION *DeviceExtension; DeviceExtension = FspDeviceExtension(DeviceObject); KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); if (0 != DeviceExtension->RefCount) { DeviceExtension->RefCount--; Delete = 0 == DeviceExtension->RefCount; } KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); ASSERT(!Delete); } static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); NTSTATUS Result; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); LARGE_INTEGER IrpTimeout; LARGE_INTEGER SecurityTimeout, DirInfoTimeout, StreamInfoTimeout; /* * Volume device initialization is a mess, because of the different ways of * creating/initializing different resources. So we will use some bits just * to track what has been initialized! */ /* is there a virtual disk? */ if (0 != FsvolDeviceExtension->FsvrtDeviceObject) { /* allocate a spare VPB so that we can be mounted on the virtual disk */ FsvolDeviceExtension->SwapVpb = FspAllocNonPagedExternal(sizeof *FsvolDeviceExtension->SwapVpb); if (0 == FsvolDeviceExtension->SwapVpb) return STATUS_INSUFFICIENT_RESOURCES; RtlZeroMemory(FsvolDeviceExtension->SwapVpb, sizeof *FsvolDeviceExtension->SwapVpb); /* reference the virtual disk device so that it will not go away while we are using it */ ObReferenceObject(FsvolDeviceExtension->FsvrtDeviceObject); FsvolDeviceExtension->InitDoneFsvrt = 1; } /* create our Ioq */ IrpTimeout.QuadPart = FsvolDeviceExtension->VolumeParams.IrpTimeout * 10000ULL; /* convert millis to nanos */ Result = FspIoqCreate( FsvolDeviceExtension->VolumeParams.IrpCapacity, &IrpTimeout, FspIopCompleteCanceledIrp, &FsvolDeviceExtension->Ioq); if (!NT_SUCCESS(Result)) return Result; FsvolDeviceExtension->InitDoneIoq = 1; /* create our security meta cache */ SecurityTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); /* convert millis to nanos */ Result = FspMetaCacheCreate( FspFsvolDeviceSecurityCacheCapacity, FspFsvolDeviceSecurityCacheItemSizeMax, &SecurityTimeout, &FsvolDeviceExtension->SecurityCache); if (!NT_SUCCESS(Result)) return Result; FsvolDeviceExtension->InitDoneSec = 1; /* create our directory meta cache */ DirInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); /* convert millis to nanos */ Result = FspMetaCacheCreate( FspFsvolDeviceDirInfoCacheCapacity, FspFsvolDeviceDirInfoCacheItemSizeMax, &DirInfoTimeout, &FsvolDeviceExtension->DirInfoCache); if (!NT_SUCCESS(Result)) return Result; FsvolDeviceExtension->InitDoneDir = 1; /* create our stream info meta cache */ StreamInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); /* convert millis to nanos */ Result = FspMetaCacheCreate( FspFsvolDeviceStreamInfoCacheCapacity, FspFsvolDeviceStreamInfoCacheItemSizeMax, &StreamInfoTimeout, &FsvolDeviceExtension->StreamInfoCache); if (!NT_SUCCESS(Result)) return Result; FsvolDeviceExtension->InitDoneStrm = 1; /* initialize the FSRTL Notify mechanism */ Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync); if (!NT_SUCCESS(Result)) return Result; InitializeListHead(&FsvolDeviceExtension->NotifyList); FsvolDeviceExtension->InitDoneNotify = 1; /* initialize our context table */ ExInitializeResourceLite(&FsvolDeviceExtension->FileRenameResource); ExInitializeResourceLite(&FsvolDeviceExtension->ContextTableResource); RtlInitializeGenericTableAvl(&FsvolDeviceExtension->ContextByNameTable, FspFsvolDeviceCompareContextByName, FspFsvolDeviceAllocateContextByName, FspFsvolDeviceFreeContextByName, 0); FsvolDeviceExtension->InitDoneCtxTab = 1; /* initialize our timer routine and start our expiration timer */ #pragma prefast(suppress:28133, "We are a filesystem: we do not have AddDevice") Result = IoInitializeTimer(DeviceObject, FspFsvolDeviceTimerRoutine, 0); if (!NT_SUCCESS(Result)) return Result; KeInitializeSpinLock(&FsvolDeviceExtension->ExpirationLock); ExInitializeWorkItem(&FsvolDeviceExtension->ExpirationWorkItem, FspFsvolDeviceExpirationRoutine, DeviceObject); IoStartTimer(DeviceObject); FsvolDeviceExtension->InitDoneTimer = 1; /* initialize the volume information */ KeInitializeSpinLock(&FsvolDeviceExtension->InfoSpinLock); FsvolDeviceExtension->InitDoneInfo = 1; return STATUS_SUCCESS; } static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); /* * First things first: stop our timer. * * Our IoTimer routine will NOT be called again after IoStopTimer() returns. * However a work item may be in flight. For this reason our IoTimer routine * references our DeviceObject before queueing work items. */ if (FsvolDeviceExtension->InitDoneTimer) IoStopTimer(DeviceObject); /* uninitialize the FSRTL Notify mechanism */ if (FsvolDeviceExtension->InitDoneNotify) { FspNotifyCleanupAll( FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList); FspNotifyUninitializeSync(&FsvolDeviceExtension->NotifySync); } /* delete the stream info meta cache */ if (FsvolDeviceExtension->InitDoneStrm) FspMetaCacheDelete(FsvolDeviceExtension->StreamInfoCache); /* delete the directory meta cache */ if (FsvolDeviceExtension->InitDoneDir) FspMetaCacheDelete(FsvolDeviceExtension->DirInfoCache); /* delete the security meta cache */ if (FsvolDeviceExtension->InitDoneSec) FspMetaCacheDelete(FsvolDeviceExtension->SecurityCache); /* delete the Ioq */ if (FsvolDeviceExtension->InitDoneIoq) FspIoqDelete(FsvolDeviceExtension->Ioq); if (FsvolDeviceExtension->InitDoneCtxTab) { /* * FspDeviceFreeContext/FspDeviceFreeContextByName is a no-op, so it is not necessary * to enumerate and delete all entries in the ContextTable. */ ExDeleteResourceLite(&FsvolDeviceExtension->ContextTableResource); ExDeleteResourceLite(&FsvolDeviceExtension->FileRenameResource); } /* is there a virtual disk? */ if (FsvolDeviceExtension->InitDoneFsvrt) { /* dereference the virtual volume device so that it can now go away */ ObDereferenceObject(FsvolDeviceExtension->FsvrtDeviceObject); /* free the spare VPB if we still have it */ if (0 != FsvolDeviceExtension->SwapVpb) FspFreeExternal(FsvolDeviceExtension->SwapVpb); } } static VOID FspFsvolDeviceTimerRoutine(PDEVICE_OBJECT DeviceObject, PVOID Context) { // !PAGED_CODE(); /* * This routine runs at DPC level. Reference our DeviceObject and queue a work item * so that we can do our processing at Passive level. Only do so if the work item * is not already in flight (otherwise we could requeue the same work item). */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); if (!FspDeviceReferenceAtDpcLevel(DeviceObject)) return; BOOLEAN ExpirationInProgress; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); KeAcquireSpinLockAtDpcLevel(&FsvolDeviceExtension->ExpirationLock); ExpirationInProgress = FsvolDeviceExtension->ExpirationInProgress; if (!ExpirationInProgress) { FsvolDeviceExtension->ExpirationInProgress = TRUE; ExQueueWorkItem(&FsvolDeviceExtension->ExpirationWorkItem, DelayedWorkQueue); } KeReleaseSpinLockFromDpcLevel(&FsvolDeviceExtension->ExpirationLock); if (ExpirationInProgress) FspDeviceDereferenceFromDpcLevel(DeviceObject); } static VOID FspFsvolDeviceExpirationRoutine(PVOID Context) { // !PAGED_CODE(); PDEVICE_OBJECT DeviceObject = Context; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); UINT64 InterruptTime; KIRQL Irql; InterruptTime = KeQueryInterruptTime(); FspMetaCacheInvalidateExpired(FsvolDeviceExtension->SecurityCache, InterruptTime); FspMetaCacheInvalidateExpired(FsvolDeviceExtension->DirInfoCache, InterruptTime); FspMetaCacheInvalidateExpired(FsvolDeviceExtension->StreamInfoCache, InterruptTime); FspIoqRemoveExpired(FsvolDeviceExtension->Ioq, InterruptTime); KeAcquireSpinLock(&FsvolDeviceExtension->ExpirationLock, &Irql); FsvolDeviceExtension->ExpirationInProgress = FALSE; KeReleaseSpinLock(&FsvolDeviceExtension->ExpirationLock, Irql); FspDeviceDereference(DeviceObject); } VOID FspFsvolDeviceFileRenameAcquireShared(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); ExAcquireResourceSharedLite(&FsvolDeviceExtension->FileRenameResource, TRUE); } VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); ExAcquireResourceExclusiveLite(&FsvolDeviceExtension->FileRenameResource, TRUE); } VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); Owner = (PVOID)((UINT_PTR)Owner | 3); ExSetResourceOwnerPointer(&FsvolDeviceExtension->FileRenameResource, Owner); } VOID FspFsvolDeviceFileRenameRelease(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); ExReleaseResourceLite(&FsvolDeviceExtension->FileRenameResource); } VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); Owner = (PVOID)((UINT_PTR)Owner | 3); if (ExIsResourceAcquiredLite(&FsvolDeviceExtension->FileRenameResource)) ExReleaseResourceLite(&FsvolDeviceExtension->FileRenameResource); else ExReleaseResourceForThreadLite(&FsvolDeviceExtension->FileRenameResource, (ERESOURCE_THREAD)Owner); } VOID FspFsvolDeviceLockContextTable(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); ExAcquireResourceExclusiveLite(&FsvolDeviceExtension->ContextTableResource, TRUE); } VOID FspFsvolDeviceUnlockContextTable(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); ExReleaseResourceLite(&FsvolDeviceExtension->ContextTableResource); } NTSTATUS FspFsvolDeviceCopyContextByNameList(PDEVICE_OBJECT DeviceObject, PVOID **PContexts, PULONG PContextCount) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT_DATA *Data; PVOID *Contexts; ULONG ContextCount, Index; *PContexts = 0; *PContextCount = 0; ContextCount = RtlNumberGenericTableElementsAvl(&FsvolDeviceExtension->ContextByNameTable); Contexts = FspAlloc(sizeof(PVOID) * ContextCount); if (0 == Contexts) return STATUS_INSUFFICIENT_RESOURCES; Index = 0; Data = RtlEnumerateGenericTableAvl(&FsvolDeviceExtension->ContextByNameTable, TRUE); while (Index < ContextCount && 0 != Data) { Contexts[Index++] = Data->Context; Data = RtlEnumerateGenericTableAvl(&FsvolDeviceExtension->ContextByNameTable, FALSE); } *PContexts = Contexts; *PContextCount = Index; return STATUS_SUCCESS; } VOID FspFsvolDeviceDeleteContextByNameList(PVOID *Contexts, ULONG ContextCount) { PAGED_CODE(); FspFree(Contexts); } PVOID FspFsvolDeviceEnumerateContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName, BOOLEAN SubpathOnly, PVOID *PRestartKey) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); BOOLEAN CaseInsensitive = 0 == FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch; FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT_DATA *Result; ULONG DeleteCount = 0; if (0 != *PRestartKey) SubpathOnly = FALSE; Result = RtlEnumerateGenericTableLikeADirectory(&FsvolDeviceExtension->ContextByNameTable, 0, 0, SubpathOnly, PRestartKey, &DeleteCount, &FileName); if (0 != Result && RtlPrefixUnicodeString(FileName, Result->FileName, CaseInsensitive) && FileName->Length < Result->FileName->Length && '\\' == Result->FileName->Buffer[FileName->Length / sizeof(WCHAR)]) return Result->Context; else return 0; } PVOID FspFsvolDeviceLookupContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT_DATA *Result; Result = RtlLookupElementGenericTableAvl(&FsvolDeviceExtension->ContextByNameTable, &FileName); return 0 != Result ? Result->Context : 0; } PVOID FspFsvolDeviceInsertContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName, PVOID Context, FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT *ElementStorage, PBOOLEAN PInserted) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT_DATA *Result, Element = { 0 }; ASSERT(0 != ElementStorage); Element.FileName = FileName; Element.Context = Context; FsvolDeviceExtension->ContextByNameTableElementStorage = ElementStorage; Result = RtlInsertElementGenericTableAvl(&FsvolDeviceExtension->ContextByNameTable, &Element, sizeof Element, PInserted); FsvolDeviceExtension->ContextByNameTableElementStorage = 0; ASSERT(0 != Result); return Result->Context; } VOID FspFsvolDeviceDeleteContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING FileName, PBOOLEAN PDeleted) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); BOOLEAN Deleted; Deleted = RtlDeleteElementGenericTableAvl(&FsvolDeviceExtension->ContextByNameTable, &FileName); if (0 != PDeleted) *PDeleted = Deleted; } static RTL_GENERIC_COMPARE_RESULTS NTAPI FspFsvolDeviceCompareContextByName( PRTL_AVL_TABLE Table, PVOID FirstElement, PVOID SecondElement) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = CONTAINING_RECORD(Table, FSP_FSVOL_DEVICE_EXTENSION, ContextByNameTable); BOOLEAN CaseInsensitive = 0 == FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch; PUNICODE_STRING FirstFileName = *(PUNICODE_STRING *)FirstElement; PUNICODE_STRING SecondFileName = *(PUNICODE_STRING *)SecondElement; LONG ComparisonResult; ComparisonResult = RtlCompareUnicodeString(FirstFileName, SecondFileName, CaseInsensitive); if (0 > ComparisonResult) return GenericLessThan; else if (0 < ComparisonResult) return GenericGreaterThan; else return GenericEqual; } static PVOID NTAPI FspFsvolDeviceAllocateContextByName( PRTL_AVL_TABLE Table, CLONG ByteSize) { PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = CONTAINING_RECORD(Table, FSP_FSVOL_DEVICE_EXTENSION, ContextByNameTable); ASSERT(sizeof(FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT) == ByteSize); return FsvolDeviceExtension->ContextByNameTableElementStorage; } static VOID NTAPI FspFsvolDeviceFreeContextByName( PRTL_AVL_TABLE Table, PVOID Buffer) { PAGED_CODE(); } VOID FspFsvolDeviceGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_INFO *VolumeInfo) { // !PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_FSCTL_VOLUME_INFO VolumeInfoNp; KIRQL Irql; KeAcquireSpinLock(&FsvolDeviceExtension->InfoSpinLock, &Irql); VolumeInfoNp = FsvolDeviceExtension->VolumeInfo; KeReleaseSpinLock(&FsvolDeviceExtension->InfoSpinLock, Irql); *VolumeInfo = VolumeInfoNp; } #pragma warning(push) #pragma warning(disable:4701) /* disable idiotic warning! */ BOOLEAN FspFsvolDeviceTryGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_INFO *VolumeInfo) { // !PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_FSCTL_VOLUME_INFO VolumeInfoNp; KIRQL Irql; BOOLEAN Result; KeAcquireSpinLock(&FsvolDeviceExtension->InfoSpinLock, &Irql); if (FspExpirationTimeValid(FsvolDeviceExtension->InfoExpirationTime)) { VolumeInfoNp = FsvolDeviceExtension->VolumeInfo; Result = TRUE; } else Result = FALSE; KeReleaseSpinLock(&FsvolDeviceExtension->InfoSpinLock, Irql); if (Result) *VolumeInfo = VolumeInfoNp; return Result; } #pragma warning(pop) VOID FspFsvolDeviceSetVolumeInfo(PDEVICE_OBJECT DeviceObject, const FSP_FSCTL_VOLUME_INFO *VolumeInfo) { // !PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_FSCTL_VOLUME_INFO VolumeInfoNp = *VolumeInfo; KIRQL Irql; KeAcquireSpinLock(&FsvolDeviceExtension->InfoSpinLock, &Irql); FsvolDeviceExtension->VolumeInfo = VolumeInfoNp; FsvolDeviceExtension->InfoExpirationTime = FspExpirationTimeFromMillis( FsvolDeviceExtension->VolumeParams.FileInfoTimeout); KeReleaseSpinLock(&FsvolDeviceExtension->InfoSpinLock, Irql); } VOID FspFsvolDeviceInvalidateVolumeInfo(PDEVICE_OBJECT DeviceObject) { // !PAGED_CODE(); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); KIRQL Irql; KeAcquireSpinLock(&FsvolDeviceExtension->InfoSpinLock, &Irql); FsvolDeviceExtension->InfoExpirationTime = 0; KeReleaseSpinLock(&FsvolDeviceExtension->InfoSpinLock, Irql); } NTSTATUS FspDeviceCopyList( PDEVICE_OBJECT **PDeviceObjects, PULONG PDeviceObjectCount) { PAGED_CODE(); PDEVICE_OBJECT *DeviceObjects = 0; ULONG DeviceObjectCount = 0; while (STATUS_BUFFER_TOO_SMALL == IoEnumerateDeviceObjectList(FspDriverObject, DeviceObjects, sizeof *DeviceObjects * DeviceObjectCount, &DeviceObjectCount)) { if (0 != DeviceObjects) FspFree(DeviceObjects); DeviceObjects = FspAllocNonPaged(sizeof *DeviceObjects * DeviceObjectCount); if (0 == DeviceObjects) return STATUS_INSUFFICIENT_RESOURCES; RtlZeroMemory(DeviceObjects, sizeof *DeviceObjects * DeviceObjectCount); } *PDeviceObjects = DeviceObjects; *PDeviceObjectCount = DeviceObjectCount; return STATUS_SUCCESS; } VOID FspDeviceDeleteList( PDEVICE_OBJECT *DeviceObjects, ULONG DeviceObjectCount) { PAGED_CODE(); for (ULONG i = 0; DeviceObjectCount > i; i++) ObDereferenceObject(DeviceObjects[i]); FspFree(DeviceObjects); } VOID FspDeviceDeleteAll(VOID) { PAGED_CODE(); NTSTATUS Result; PDEVICE_OBJECT *DeviceObjects = 0; ULONG DeviceObjectCount = 0; Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount); if (!NT_SUCCESS(Result)) return; for (ULONG i = 0; DeviceObjectCount > i; i++) FspDeviceDelete(DeviceObjects[i]); FspDeviceDeleteList(DeviceObjects, DeviceObjectCount); } ERESOURCE FspDeviceGlobalResource;