/** * @file sys/util.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 FspCreateGuid(GUID *Guid); NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject); NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length); static NTSTATUS FspSendSetInformationIrpCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0); NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress, ULONG ExtraPriorityFlags); NTSTATUS FspCcInitializeCacheMap(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes, BOOLEAN PinAccess, PCACHE_MANAGER_CALLBACKS Callbacks, PVOID CallbackContext); NTSTATUS FspCcSetFileSizes(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes); NTSTATUS FspCcCopyRead(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, PVOID Buffer, PIO_STATUS_BLOCK IoStatus); NTSTATUS FspCcCopyWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, PVOID Buffer); NTSTATUS FspCcMdlRead(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus); NTSTATUS FspCcMdlReadComplete(PFILE_OBJECT FileObject, PMDL MdlChain); NTSTATUS FspCcPrepareMdlWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus); NTSTATUS FspCcMdlWriteComplete(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain); NTSTATUS FspCcFlushCache(PSECTION_OBJECT_POINTERS SectionObjectPointer, PLARGE_INTEGER FileOffset, ULONG Length, PIO_STATUS_BLOCK IoStatus); NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor); NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync); NTSTATUS FspNotifyFullChangeDirectory( PNOTIFY_SYNC NotifySync, PLIST_ENTRY NotifyList, PVOID FsContext, PSTRING FullDirectoryName, BOOLEAN WatchTree, BOOLEAN IgnoreBuffer, ULONG CompletionFilter, PIRP NotifyIrp, PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback, PSECURITY_SUBJECT_CONTEXT SubjectContext); NTSTATUS FspNotifyFullReportChange( PNOTIFY_SYNC NotifySync, PLIST_ENTRY NotifyList, PSTRING FullTargetName, USHORT TargetNameOffset, PSTRING StreamName, PSTRING NormalizedParentName, ULONG FilterMatch, ULONG Action, PVOID TargetContext); VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem); static WORKER_THREAD_ROUTINE FspExecuteSynchronousWorkItemRoutine; VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspQueueDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, LARGE_INTEGER Delay); static KDEFERRED_ROUTINE FspQueueDelayedWorkItemDPC; BOOLEAN FspSafeMdlCheck(PMDL Mdl); NTSTATUS FspSafeMdlCreate(PMDL UserMdl, LOCK_OPERATION Operation, FSP_SAFE_MDL **PSafeMdl); VOID FspSafeMdlCopyBack(FSP_SAFE_MDL *SafeMdl); VOID FspSafeMdlDelete(FSP_SAFE_MDL *SafeMdl); NTSTATUS FspIrpHook(PIRP Irp, PIO_COMPLETION_ROUTINE CompletionRoutine, PVOID OwnContext); VOID FspIrpHookReset(PIRP Irp); PVOID FspIrpHookContext(PVOID Context); NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspCreateGuid) #pragma alloc_text(PAGE, FspGetDeviceObjectPointer) #pragma alloc_text(PAGE, FspSendSetInformationIrp) #pragma alloc_text(PAGE, FspBufferUserBuffer) #pragma alloc_text(PAGE, FspLockUserBuffer) #pragma alloc_text(PAGE, FspMapLockedPagesInUserMode) #pragma alloc_text(PAGE, FspCcInitializeCacheMap) #pragma alloc_text(PAGE, FspCcSetFileSizes) #pragma alloc_text(PAGE, FspCcCopyRead) #pragma alloc_text(PAGE, FspCcCopyWrite) #pragma alloc_text(PAGE, FspCcMdlRead) #pragma alloc_text(PAGE, FspCcMdlReadComplete) #pragma alloc_text(PAGE, FspCcPrepareMdlWrite) #pragma alloc_text(PAGE, FspCcMdlWriteComplete) #pragma alloc_text(PAGE, FspCcFlushCache) #pragma alloc_text(PAGE, FspQuerySecurityDescriptorInfo) #pragma alloc_text(PAGE, FspNotifyInitializeSync) #pragma alloc_text(PAGE, FspNotifyFullChangeDirectory) #pragma alloc_text(PAGE, FspNotifyFullReportChange) #pragma alloc_text(PAGE, FspInitializeSynchronousWorkItem) #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItem) #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItemRoutine) #pragma alloc_text(PAGE, FspInitializeDelayedWorkItem) #pragma alloc_text(PAGE, FspQueueDelayedWorkItem) #pragma alloc_text(PAGE, FspSafeMdlCheck) #pragma alloc_text(PAGE, FspSafeMdlCreate) #pragma alloc_text(PAGE, FspSafeMdlCopyBack) #pragma alloc_text(PAGE, FspSafeMdlDelete) #pragma alloc_text(PAGE, FspIrpHook) #pragma alloc_text(PAGE, FspIrpHookReset) // !#pragma alloc_text(PAGE, FspIrpHookContext) // !#pragma alloc_text(PAGE, FspIrpHookNext) #endif static const LONG Delays[] = { -100, -200, -300, -400, -500, -1000, }; PVOID FspAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag) { // !PAGED_CODE(); PVOID Result; LARGE_INTEGER Delay; for (ULONG i = 0, n = sizeof(Delays) / sizeof(Delays[0]);; i++) { Result = DEBUGTEST(95) ? ExAllocatePoolWithTag(PoolType, Size, Tag) : 0; if (0 != Result) return Result; Delay.QuadPart = n > i ? Delays[i] : Delays[n - 1]; KeDelayExecutionThread(KernelMode, FALSE, &Delay); } } PVOID FspAllocateIrpMustSucceed(CCHAR StackSize) { // !PAGED_CODE(); PIRP Result; LARGE_INTEGER Delay; for (ULONG i = 0, n = sizeof(Delays) / sizeof(Delays[0]);; i++) { Result = DEBUGTEST(95) ? IoAllocateIrp(StackSize, FALSE) : 0; if (0 != Result) return Result; Delay.QuadPart = n > i ? Delays[i] : Delays[n - 1]; KeDelayExecutionThread(KernelMode, FALSE, &Delay); } } NTSTATUS FspCreateGuid(GUID *Guid) { PAGED_CODE(); NTSTATUS Result; int Retries = 3; do { Result = ExUuidCreate(Guid); } while (!NT_SUCCESS(Result) && 0 < --Retries); return Result; } NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject) { PAGED_CODE(); UNICODE_STRING PartialName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Handle; NTSTATUS Result; PartialName.Length = 0; PartialName.MaximumLength = ObjectName->Length; PartialName.Buffer = ObjectName->Buffer; Result = STATUS_NO_SUCH_DEVICE; while (PartialName.MaximumLength > PartialName.Length) { while (PartialName.MaximumLength > PartialName.Length && L'\\' == PartialName.Buffer[PartialName.Length / sizeof(WCHAR)]) PartialName.Length += sizeof(WCHAR); while (PartialName.MaximumLength > PartialName.Length && L'\\' != PartialName.Buffer[PartialName.Length / sizeof(WCHAR)]) PartialName.Length += sizeof(WCHAR); Result = IoGetDeviceObjectPointer(&PartialName, DesiredAccess, PFileObject, PDeviceObject); if (NT_SUCCESS(Result)) { *PFileNameIndex = PartialName.Length; break; } InitializeObjectAttributes(&ObjectAttributes, &PartialName, OBJ_KERNEL_HANDLE, 0, 0); Result = ZwOpenDirectoryObject(&Handle, 0, &ObjectAttributes); if (!NT_SUCCESS(Result)) { Result = ZwOpenSymbolicLinkObject(&Handle, 0, &ObjectAttributes); if (!NT_SUCCESS(Result)) { Result = STATUS_NO_SUCH_DEVICE; break; } } ZwClose(Handle); } return Result; } typedef struct { IO_STATUS_BLOCK IoStatus; KEVENT Event; } FSP_SEND_SET_INFORMATION_IRP_CONTEXT; NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length) { PAGED_CODE(); ASSERT( FileAllocationInformation == FileInformationClass || FileEndOfFileInformation == FileInformationClass); NTSTATUS Result; PIRP Irp; PIO_STACK_LOCATION IrpSp; FSP_SEND_SET_INFORMATION_IRP_CONTEXT Context; if (0 == DeviceObject) DeviceObject = IoGetRelatedDeviceObject(FileObject); Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (0 == Irp) return STATUS_INSUFFICIENT_RESOURCES; IrpSp = IoGetNextIrpStackLocation(Irp); Irp->RequestorMode = KernelMode; Irp->AssociatedIrp.SystemBuffer = FileInformation; IrpSp->MajorFunction = IRP_MJ_SET_INFORMATION; IrpSp->FileObject = FileObject; IrpSp->Parameters.SetFile.FileInformationClass = FileInformationClass; IrpSp->Parameters.SetFile.Length = FileInformationClass; IoSetCompletionRoutine(Irp, FspSendSetInformationIrpCompletion, &Context, TRUE, TRUE, TRUE); KeInitializeEvent(&Context.Event, NotificationEvent, FALSE); Result = IoCallDriver(DeviceObject, Irp); if (STATUS_PENDING == Result) KeWaitForSingleObject(&Context.Event, Executive, KernelMode, FALSE, 0); return NT_SUCCESS(Result) ? Context.IoStatus.Status : Result; } static NTSTATUS FspSendSetInformationIrpCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0) { // !PAGED_CODE(); FSP_SEND_SET_INFORMATION_IRP_CONTEXT *Context = Context0; Context->IoStatus = Irp->IoStatus; KeSetEvent(&Context->Event, 1, FALSE); IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation) { PAGED_CODE(); if (0 == Length || 0 != Irp->AssociatedIrp.SystemBuffer) return STATUS_SUCCESS; PVOID SystemBuffer = FspAllocNonPagedExternal(Length); if (0 == SystemBuffer) return STATUS_INSUFFICIENT_RESOURCES; if (IoReadAccess == Operation) { try { RtlCopyMemory(SystemBuffer, Irp->UserBuffer, Length); } except (EXCEPTION_EXECUTE_HANDLER) { FspFree(SystemBuffer); NTSTATUS Result = GetExceptionCode(); return FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; } } else RtlZeroMemory(SystemBuffer, Length); Irp->AssociatedIrp.SystemBuffer = SystemBuffer; Irp->Flags |= (IoReadAccess == Operation ? 0 : IRP_INPUT_OPERATION) | IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; return STATUS_SUCCESS; } NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation) { PAGED_CODE(); if (0 == Length || 0 != Irp->MdlAddress) return STATUS_SUCCESS; PMDL Mdl = IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, 0); if (0 == Mdl) return STATUS_INSUFFICIENT_RESOURCES; try { MmProbeAndLockPages(Mdl, Irp->RequestorMode, Operation); } except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(Mdl); NTSTATUS Result = GetExceptionCode(); return FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; } Irp->MdlAddress = Mdl; return STATUS_SUCCESS; } NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress, ULONG ExtraPriorityFlags) { PAGED_CODE(); try { *PAddress = MmMapLockedPagesSpecifyCache(Mdl, UserMode, MmCached, 0, FALSE, NormalPagePriority | ExtraPriorityFlags); return 0 != *PAddress ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; } except (EXCEPTION_EXECUTE_HANDLER) { *PAddress = 0; return GetExceptionCode(); } } NTSTATUS FspCcInitializeCacheMap(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes, BOOLEAN PinAccess, PCACHE_MANAGER_CALLBACKS Callbacks, PVOID CallbackContext) { PAGED_CODE(); try { CcInitializeCacheMap(FileObject, FileSizes, PinAccess, Callbacks, CallbackContext); return STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } NTSTATUS FspCcSetFileSizes(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes) { PAGED_CODE(); try { CcSetFileSizes(FileObject, FileSizes); return STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } NTSTATUS FspCcCopyRead(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, PVOID Buffer, PIO_STATUS_BLOCK IoStatus) { PAGED_CODE(); NTSTATUS Result; try { BOOLEAN Success = CcCopyRead(FileObject, FileOffset, Length, Wait, Buffer, IoStatus); Result = Success ? STATUS_SUCCESS : STATUS_PENDING; } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); IoStatus->Information = 0; IoStatus->Status = Result; } return Result; } NTSTATUS FspCcCopyWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, PVOID Buffer) { PAGED_CODE(); try { BOOLEAN Success = CcCopyWrite(FileObject, FileOffset, Length, Wait, Buffer); return Success ? STATUS_SUCCESS : STATUS_PENDING; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } NTSTATUS FspCcMdlRead(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus) { PAGED_CODE(); try { CcMdlRead(FileObject, FileOffset, Length, PMdlChain, IoStatus); return IoStatus->Status; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } NTSTATUS FspCcMdlReadComplete(PFILE_OBJECT FileObject, PMDL MdlChain) { PAGED_CODE(); try { CcMdlReadComplete(FileObject, MdlChain); return STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } NTSTATUS FspCcPrepareMdlWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus) { PAGED_CODE(); try { CcPrepareMdlWrite(FileObject, FileOffset, Length, PMdlChain, IoStatus); return IoStatus->Status; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } NTSTATUS FspCcMdlWriteComplete(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain) { PAGED_CODE(); try { CcMdlWriteComplete(FileObject, FileOffset, MdlChain); return STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { CcMdlWriteAbort(FileObject, MdlChain); return GetExceptionCode(); } } NTSTATUS FspCcFlushCache(PSECTION_OBJECT_POINTERS SectionObjectPointer, PLARGE_INTEGER FileOffset, ULONG Length, PIO_STATUS_BLOCK IoStatus) { PAGED_CODE(); NTSTATUS Result; try { CcFlushCache(SectionObjectPointer, FileOffset, Length, IoStatus); Result = IoStatus->Status; } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); IoStatus->Information = 0; IoStatus->Status = Result; } return Result; } NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor) { PAGED_CODE(); NTSTATUS Result; try { Result = SeQuerySecurityDescriptorInfo(&SecurityInformation, SecurityDescriptor, PLength, &ObjectsSecurityDescriptor); } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; } return STATUS_BUFFER_TOO_SMALL == Result ? STATUS_BUFFER_OVERFLOW : Result; } NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync) { PAGED_CODE(); NTSTATUS Result; try { FsRtlNotifyInitializeSync(NotifySync); Result = STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); } return Result; } NTSTATUS FspNotifyFullChangeDirectory( PNOTIFY_SYNC NotifySync, PLIST_ENTRY NotifyList, PVOID FsContext, PSTRING FullDirectoryName, BOOLEAN WatchTree, BOOLEAN IgnoreBuffer, ULONG CompletionFilter, PIRP NotifyIrp, PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback, PSECURITY_SUBJECT_CONTEXT SubjectContext) { PAGED_CODE(); NTSTATUS Result; try { FsRtlNotifyFullChangeDirectory( NotifySync, NotifyList, FsContext, FullDirectoryName, WatchTree, IgnoreBuffer, CompletionFilter, NotifyIrp, TraverseCallback, SubjectContext); Result = STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); } return Result; } NTSTATUS FspNotifyFullReportChange( PNOTIFY_SYNC NotifySync, PLIST_ENTRY NotifyList, PSTRING FullTargetName, USHORT TargetNameOffset, PSTRING StreamName, PSTRING NormalizedParentName, ULONG FilterMatch, ULONG Action, PVOID TargetContext) { PAGED_CODE(); NTSTATUS Result; try { FsRtlNotifyFullReportChange( NotifySync, NotifyList, FullTargetName, TargetNameOffset, StreamName, NormalizedParentName, FilterMatch, Action, TargetContext); Result = STATUS_SUCCESS; } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); } return Result; } VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context) { PAGED_CODE(); KeInitializeEvent(&SynchronousWorkItem->Event, NotificationEvent, FALSE); SynchronousWorkItem->Routine = Routine; SynchronousWorkItem->Context = Context; ExInitializeWorkItem(&SynchronousWorkItem->WorkQueueItem, FspExecuteSynchronousWorkItemRoutine, SynchronousWorkItem); } VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem) { PAGED_CODE(); ExQueueWorkItem(&SynchronousWorkItem->WorkQueueItem, CriticalWorkQueue); NTSTATUS Result; Result = KeWaitForSingleObject(&SynchronousWorkItem->Event, Executive, KernelMode, FALSE, 0); ASSERT(STATUS_SUCCESS == Result); } static VOID FspExecuteSynchronousWorkItemRoutine(PVOID Context) { PAGED_CODE(); FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem = Context; SynchronousWorkItem->Routine(SynchronousWorkItem->Context); KeSetEvent(&SynchronousWorkItem->Event, 1, FALSE); } VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context) { PAGED_CODE(); KeInitializeTimer(&DelayedWorkItem->Timer); KeInitializeDpc(&DelayedWorkItem->Dpc, FspQueueDelayedWorkItemDPC, DelayedWorkItem); ExInitializeWorkItem(&DelayedWorkItem->WorkQueueItem, Routine, Context); } VOID FspQueueDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, LARGE_INTEGER Delay) { PAGED_CODE(); KeSetTimer(&DelayedWorkItem->Timer, Delay, &DelayedWorkItem->Dpc); } static VOID FspQueueDelayedWorkItemDPC(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { // !PAGED_CODE(); FSP_DELAYED_WORK_ITEM *DelayedWorkItem = DeferredContext; ExQueueWorkItem(&DelayedWorkItem->WorkQueueItem, DelayedWorkQueue); } BOOLEAN FspSafeMdlCheck(PMDL Mdl) { PAGED_CODE(); return 0 == MmGetMdlByteOffset(Mdl) && 0 == BYTE_OFFSET(MmGetMdlByteCount(Mdl)); } NTSTATUS FspSafeMdlCreate(PMDL UserMdl, LOCK_OPERATION Operation, FSP_SAFE_MDL **PSafeMdl) { PAGED_CODE(); NTSTATUS Result; PVOID VirtualAddress = MmGetSystemAddressForMdlSafe(UserMdl, NormalPagePriority); ULONG ByteCount = MmGetMdlByteCount(UserMdl); ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, ByteCount); FSP_SAFE_MDL *SafeMdl; PMDL TempMdl; PPFN_NUMBER UserPfnArray, SafePfnArray, TempPfnArray; ULONG ByteOffsetBgn0, ByteOffsetEnd0, ByteOffsetEnd1; BOOLEAN Buffer0, Buffer1; ULONG BufferPageCount; ASSERT(0 != PageCount); ASSERT(FlagOn(UserMdl->MdlFlags, MDL_PAGES_LOCKED)); *PSafeMdl = 0; SafeMdl = FspAllocNonPaged(sizeof *SafeMdl); if (0 == SafeMdl) { Result = STATUS_INSUFFICIENT_RESOURCES; goto exit; } RtlZeroMemory(SafeMdl, sizeof *SafeMdl); SafeMdl->Operation = Operation; SafeMdl->Mdl = IoAllocateMdl(VirtualAddress, ByteCount, FALSE, FALSE, 0); if (0 == SafeMdl->Mdl) { Result = STATUS_INSUFFICIENT_RESOURCES; goto exit; } #pragma prefast(suppress:28145, "We are a filesystem: ok to access MdlFlags") SafeMdl->Mdl->MdlFlags |= MDL_PAGES_LOCKED; UserPfnArray = MmGetMdlPfnArray(UserMdl); SafePfnArray = MmGetMdlPfnArray(SafeMdl->Mdl); RtlCopyMemory(SafePfnArray, UserPfnArray, PageCount * sizeof(PFN_NUMBER)); /* * Possible cases: * * ----+---------+---------+---- * * *---------* + * + *----* + * *----* + + * + *---* + + * *--------...--------* * + *---...--------* * *--------...---* + * + *---...---* + */ if (1 == PageCount) { ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); ByteOffsetEnd0 = BYTE_OFFSET((PUINT8)VirtualAddress + ByteCount - 1) + 1; ByteOffsetEnd1 = 0; Buffer0 = 0 != ByteOffsetBgn0 || PAGE_SIZE != ByteOffsetEnd0; Buffer1 = FALSE; } else { ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); ByteOffsetEnd0 = PAGE_SIZE; ByteOffsetEnd1 = BYTE_OFFSET((PUINT8)VirtualAddress + ByteCount - 1) + 1; Buffer0 = 0 != ByteOffsetBgn0; Buffer1 = PAGE_SIZE != ByteOffsetEnd1; } BufferPageCount = Buffer0 + Buffer1; if (0 < BufferPageCount) { SafeMdl->Buffer = FspAllocNonPaged(PAGE_SIZE * BufferPageCount); if (0 == SafeMdl->Buffer) { Result = STATUS_INSUFFICIENT_RESOURCES; goto exit; } TempMdl = IoAllocateMdl(SafeMdl->Buffer, PAGE_SIZE * BufferPageCount, FALSE, FALSE, 0); if (0 == TempMdl) { Result = STATUS_INSUFFICIENT_RESOURCES; goto exit; } MmBuildMdlForNonPagedPool(TempMdl); TempPfnArray = MmGetMdlPfnArray(TempMdl); if (IoReadAccess == Operation) { if (Buffer0) { RtlZeroMemory((PUINT8)SafeMdl->Buffer, ByteOffsetBgn0); RtlCopyMemory((PUINT8)SafeMdl->Buffer + ByteOffsetBgn0, (PUINT8)VirtualAddress, ByteOffsetEnd0 - ByteOffsetBgn0); RtlZeroMemory((PUINT8)SafeMdl->Buffer + ByteOffsetEnd0, PAGE_SIZE - ByteOffsetEnd0); SafePfnArray[0] = TempPfnArray[0]; } if (Buffer1) { RtlCopyMemory((PUINT8)SafeMdl->Buffer + (BufferPageCount - 1) * PAGE_SIZE, PAGE_ALIGN((PUINT8)VirtualAddress + (PageCount - 1) * PAGE_SIZE), ByteOffsetEnd1); RtlZeroMemory((PUINT8)SafeMdl->Buffer + (BufferPageCount - 1) * PAGE_SIZE + ByteOffsetEnd1, PAGE_SIZE - ByteOffsetEnd1); SafePfnArray[PageCount - 1] = TempPfnArray[BufferPageCount - 1]; } } else { RtlZeroMemory((PUINT8)SafeMdl->Buffer, PAGE_SIZE * BufferPageCount); if (Buffer0) SafePfnArray[0] = TempPfnArray[0]; if (Buffer1) SafePfnArray[PageCount - 1] = TempPfnArray[BufferPageCount - 1]; } IoFreeMdl(TempMdl); } SafeMdl->UserMdl = UserMdl; *PSafeMdl = SafeMdl; Result = STATUS_SUCCESS; exit: if (!NT_SUCCESS(Result) && 0 != SafeMdl) { if (0 != SafeMdl->Buffer) FspFree(SafeMdl->Buffer); if (0 != SafeMdl->Mdl) IoFreeMdl(SafeMdl->Mdl); FspFree(SafeMdl); } return Result; } VOID FspSafeMdlCopyBack(FSP_SAFE_MDL *SafeMdl) { PAGED_CODE(); if (IoReadAccess == SafeMdl->Operation) return; PVOID VirtualAddress = MmGetSystemAddressForMdlSafe(SafeMdl->UserMdl, NormalPagePriority); ULONG ByteCount = MmGetMdlByteCount(SafeMdl->UserMdl); ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, ByteCount); ULONG ByteOffsetBgn0, ByteOffsetEnd0, ByteOffsetEnd1; BOOLEAN Buffer0, Buffer1; ULONG BufferPageCount; /* * Possible cases: * * ----+---------+---------+---- * * *---------* + * + *----* + * *----* + + * + *---* + + * *--------...--------* * + *---...--------* * *--------...---* + * + *---...---* + */ if (1 == PageCount) { ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); ByteOffsetEnd0 = BYTE_OFFSET((PUINT8)VirtualAddress + ByteCount - 1) + 1; ByteOffsetEnd1 = 0; Buffer0 = 0 != ByteOffsetBgn0 || PAGE_SIZE != ByteOffsetEnd0; Buffer1 = FALSE; } else { ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); ByteOffsetEnd0 = PAGE_SIZE; ByteOffsetEnd1 = BYTE_OFFSET((PUINT8)VirtualAddress + ByteCount - 1) + 1; Buffer0 = 0 != ByteOffsetBgn0; Buffer1 = PAGE_SIZE != ByteOffsetEnd1; } BufferPageCount = Buffer0 + Buffer1; if (0 < BufferPageCount) { if (Buffer0) RtlCopyMemory((PUINT8)VirtualAddress, (PUINT8)SafeMdl->Buffer + ByteOffsetBgn0, ByteOffsetEnd0 - ByteOffsetBgn0); if (Buffer1) RtlCopyMemory(PAGE_ALIGN((PUINT8)VirtualAddress + (PageCount - 1) * PAGE_SIZE), (PUINT8)SafeMdl->Buffer + (BufferPageCount - 1) * PAGE_SIZE, ByteOffsetEnd1); } } VOID FspSafeMdlDelete(FSP_SAFE_MDL *SafeMdl) { PAGED_CODE(); if (0 != SafeMdl->Buffer) FspFree(SafeMdl->Buffer); if (0 != SafeMdl->Mdl) IoFreeMdl(SafeMdl->Mdl); FspFree(SafeMdl); } typedef struct { PIO_COMPLETION_ROUTINE CompletionRoutine; PVOID Context; ULONG Control; PVOID OwnContext; } FSP_IRP_HOOK_CONTEXT; NTSTATUS FspIrpHook(PIRP Irp, PIO_COMPLETION_ROUTINE CompletionRoutine, PVOID OwnContext) { PAGED_CODE(); PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); FSP_IRP_HOOK_CONTEXT *HookContext = 0; if (0 != IrpSp->CompletionRoutine || 0 != OwnContext) { HookContext = FspAllocNonPaged(sizeof *HookContext); if (0 == HookContext) return STATUS_INSUFFICIENT_RESOURCES; HookContext->CompletionRoutine = IrpSp->CompletionRoutine; HookContext->Context = IrpSp->Context; HookContext->Control = FlagOn(IrpSp->Control, SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL); HookContext->OwnContext = OwnContext; } IrpSp->CompletionRoutine = CompletionRoutine; IrpSp->Context = HookContext; SetFlag(IrpSp->Control, SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL); return STATUS_SUCCESS; } VOID FspIrpHookReset(PIRP Irp) { PAGED_CODE(); PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); FSP_IRP_HOOK_CONTEXT *HookContext = IrpSp->Context; if (0 != HookContext) { IrpSp->CompletionRoutine = HookContext->CompletionRoutine; IrpSp->Context = HookContext->Context; ClearFlag(IrpSp->Control, SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL); SetFlag(IrpSp->Control, HookContext->Control); FspFree(HookContext); } else { IrpSp->CompletionRoutine = 0; IrpSp->Context = 0; ClearFlag(IrpSp->Control, SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL); } } PVOID FspIrpHookContext(PVOID Context) { // !PAGED_CODE(); FSP_IRP_HOOK_CONTEXT *HookContext = Context; return 0 != HookContext ? HookContext->OwnContext : 0; } NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) { // !PAGED_CODE(); FSP_IRP_HOOK_CONTEXT *HookContext = Context; NTSTATUS Result; if (0 != HookContext && 0 != HookContext->CompletionRoutine && ( (NT_SUCCESS(Irp->IoStatus.Status) && FlagOn(HookContext->Control, SL_INVOKE_ON_SUCCESS)) || (!NT_SUCCESS(Irp->IoStatus.Status) && FlagOn(HookContext->Control, SL_INVOKE_ON_ERROR)) || (Irp->Cancel && FlagOn(HookContext->Control, SL_INVOKE_ON_CANCEL)))) { Result = HookContext->CompletionRoutine(DeviceObject, Irp, HookContext->Context); } else { if (Irp->PendingReturned && Irp->CurrentLocation <= Irp->StackCount) IoMarkIrpPending(Irp); Result = STATUS_SUCCESS; } if (0 != HookContext) FspFree(HookContext); return Result; }