/** * @file sys/volume.c * * @copyright 2015-2020 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 software * in accordance with the commercial license agreement provided in * conjunction with the software. The terms and conditions of any such * commercial license agreement shall govern, supersede, and render * ineffective any application of the GPLv3 license to this software, * notwithstanding of any reference thereto in the software or * associated repository. */ #include NTSTATUS FspVolumeCreate( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspVolumeCreateNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, FSP_SILO_GLOBALS *Globals); VOID FspVolumeDelete( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static VOID FspVolumeDeleteNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, FSP_SILO_GLOBALS *Globals); static WORKER_THREAD_ROUTINE FspVolumeDeleteDelayed; NTSTATUS FspVolumeMount( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspVolumeMountNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeMakeMountdev( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetNameList( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspVolumeGetNameListNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeTransact( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeTransactFsext( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeStop( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeNotify( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static WORKER_THREAD_ROUTINE FspVolumeNotifyWork; NTSTATUS FspVolumeWork( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspVolumeCreate) #pragma alloc_text(PAGE, FspVolumeCreateNoLock) // ! #pragma alloc_text(PAGE, FspVolumeDelete) // ! #pragma alloc_text(PAGE, FspVolumeDeleteNoLock) // ! #pragma alloc_text(PAGE, FspVolumeDeleteDelayed) // ! #pragma alloc_text(PAGE, FspVolumeMount) // ! #pragma alloc_text(PAGE, FspVolumeMountNoLock) #pragma alloc_text(PAGE, FspVolumeMakeMountdev) #pragma alloc_text(PAGE, FspVolumeGetName) #pragma alloc_text(PAGE, FspVolumeGetNameList) #pragma alloc_text(PAGE, FspVolumeGetNameListNoLock) #pragma alloc_text(PAGE, FspVolumeTransact) #pragma alloc_text(PAGE, FspVolumeTransactFsext) #pragma alloc_text(PAGE, FspVolumeStop) #pragma alloc_text(PAGE, FspVolumeNotify) #pragma alloc_text(PAGE, FspVolumeNotifyWork) #pragma alloc_text(PAGE, FspVolumeWork) #endif #define PREFIXW L"" FSP_FSCTL_VOLUME_PARAMS_PREFIX #define PREFIXW_SIZE (sizeof PREFIXW - sizeof(WCHAR)) NTSTATUS FspVolumeCreate( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); FSP_SILO_GLOBALS *Globals; NTSTATUS Result; FspSiloGetGlobals(&Globals); ASSERT(0 != Globals); FspDeviceGlobalLock(); Result = FspVolumeCreateNoLock(FsctlDeviceObject, Irp, IrpSp, Globals); FspDeviceGlobalUnlock(); FspSiloDereferenceGlobals(Globals); return Result; } static NTSTATUS FspVolumeCreateNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, FSP_SILO_GLOBALS *Globals) { PAGED_CODE(); ASSERT(IRP_MJ_CREATE == IrpSp->MajorFunction); ASSERT(0 == IrpSp->FileObject->RelatedFileObject); ASSERT(PREFIXW_SIZE <= IrpSp->FileObject->FileName.Length && RtlEqualMemory(PREFIXW, IrpSp->FileObject->FileName.Buffer, PREFIXW_SIZE)); ASSERT( FILE_DEVICE_DISK_FILE_SYSTEM == FsctlDeviceObject->DeviceType || FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType); NTSTATUS Result; PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FSCTL_VOLUME_PARAMS VolumeParams = { 0 }; USHORT PrefixLength = 0; GUID Guid; UNICODE_STRING DeviceSddl; UNICODE_STRING VolumeName; UNICODE_STRING FsmupDeviceName; WCHAR VolumeNameBuf[FSP_FSCTL_VOLUME_NAME_SIZE / sizeof(WCHAR)]; FSP_FSEXT_PROVIDER *Provider = 0; PDEVICE_OBJECT FsvolDeviceObject; PDEVICE_OBJECT FsvrtDeviceObject; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; /* check parameters */ if (PREFIXW_SIZE + sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) * sizeof(WCHAR) > FileObject->FileName.Length) return STATUS_INVALID_PARAMETER; /* copy the VolumeParams */ for (USHORT Index = 0, Length = sizeof(FSP_FSCTL_VOLUME_PARAMS); Length > Index; Index++) { if (PREFIXW_SIZE / sizeof(WCHAR) + Index >= FileObject->FileName.Length / sizeof(WCHAR)) break; WCHAR Value = FileObject->FileName.Buffer[PREFIXW_SIZE / sizeof(WCHAR) + Index]; if (0xF000 != (Value & 0xFF00)) return STATUS_INVALID_PARAMETER; ((PUINT8)&VolumeParams)[Index] = Value & 0xFF; } /* check VolumeParams size */ if (0 != VolumeParams.Version && PREFIXW_SIZE + VolumeParams.Version * sizeof(WCHAR) != FileObject->FileName.Length) return STATUS_INVALID_PARAMETER; /* check the VolumeParams */ if (0 == VolumeParams.SectorSize) VolumeParams.SectorSize = 512; if (0 == VolumeParams.SectorsPerAllocationUnit) VolumeParams.SectorsPerAllocationUnit = 1; if (0 == VolumeParams.MaxComponentLength) VolumeParams.MaxComponentLength = 255; if (0 == VolumeParams.TransactTimeout) VolumeParams.TransactTimeout = 24 * 60 * 60 * 1000; /* 1 day */ else if (FspFsctlTransactTimeoutMinimum > VolumeParams.TransactTimeout || VolumeParams.TransactTimeout > FspFsctlTransactTimeoutMaximum) VolumeParams.TransactTimeout = FspFsctlTransactTimeoutDefault; if (FspFsctlIrpTimeoutMinimum > VolumeParams.IrpTimeout || VolumeParams.IrpTimeout > FspFsctlIrpTimeoutMaximum) { /* special: allow the debug timeout value on all builds */ if (FspFsctlIrpTimeoutDebug != VolumeParams.IrpTimeout) VolumeParams.IrpTimeout = FspFsctlIrpTimeoutDefault; } if (FspFsctlIrpCapacityMinimum > VolumeParams.IrpCapacity || VolumeParams.IrpCapacity > FspFsctlIrpCapacityMaximum) VolumeParams.IrpCapacity = FspFsctlIrpCapacityDefault; if (sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) >= VolumeParams.Version) { VolumeParams.VolumeInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout; } else { if (!VolumeParams.VolumeInfoTimeoutValid) VolumeParams.VolumeInfoTimeout = VolumeParams.FileInfoTimeout; if (!VolumeParams.DirInfoTimeoutValid) VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; if (!VolumeParams.SecurityTimeoutValid) VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; if (!VolumeParams.StreamInfoTimeoutValid) VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; if (!VolumeParams.EaTimeoutValid) VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout; } VolumeParams.VolumeInfoTimeoutValid = 1; VolumeParams.DirInfoTimeoutValid = 1; VolumeParams.SecurityTimeoutValid = 1; VolumeParams.StreamInfoTimeoutValid = 1; VolumeParams.EaTimeoutValid = 1; if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) { VolumeParams.Prefix[sizeof VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0'; for (; L'\0' != VolumeParams.Prefix[PrefixLength]; PrefixLength++) ; for (; 0 < PrefixLength && L'\\' == VolumeParams.Prefix[PrefixLength - 1]; PrefixLength--) ; VolumeParams.Prefix[PrefixLength] = L'\0'; /* volume prefix cannot be the empty string */ if (0 == PrefixLength) return STATUS_INVALID_PARAMETER; /* volume prefix must start with exactly one backslash */ if (L'\\' != VolumeParams.Prefix[0] || L'\\' == VolumeParams.Prefix[1]) return STATUS_INVALID_PARAMETER; /* volume prefix must have at least one other backslash */ USHORT I; for (I = 1; L'\0' != VolumeParams.Prefix[I] && L'\\' != VolumeParams.Prefix[I]; I++) ; if (I == PrefixLength) return STATUS_INVALID_PARAMETER; } VolumeParams.FileSystemName[sizeof VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0'; #if !DBG /* * In Release builds we hardcode AlwaysUseDoubleBuffering for Reads as we do not want someone * to use WinFsp to crash Windows. * * See http://www.osronline.com/showthread.cfm?link=282037 */ VolumeParams.AlwaysUseDoubleBuffering = 1; #endif /* load any fsext provider */ if (0 != VolumeParams.FsextControlCode) { Provider = FspFsextProvider(VolumeParams.FsextControlCode, &Result); if (0 == Provider) return Result; } /* create volume guid */ Result = FspCreateGuid(&Guid); if (!NT_SUCCESS(Result)) return Result; /* prepare the device name and SDDL */ RtlInitUnicodeString(&DeviceSddl, L"" FSP_FSVRT_DEVICE_SDDL); RtlInitEmptyUnicodeString(&VolumeName, VolumeNameBuf, sizeof VolumeNameBuf); Result = RtlUnicodeStringPrintf(&VolumeName, L"\\Device\\Volume{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", Guid.Data1, Guid.Data2, Guid.Data3, Guid.Data4[0], Guid.Data4[1], Guid.Data4[2], Guid.Data4[3], Guid.Data4[4], Guid.Data4[5], Guid.Data4[6], Guid.Data4[7]); ASSERT(NT_SUCCESS(Result)); VolumeName.MaximumLength = VolumeName.Length; /* create the volume (and virtual disk) device(s) */ Result = FspDeviceCreate(FspFsvolDeviceExtensionKind, 0 == Provider ? 0 : Provider->DeviceExtensionSize, FsctlDeviceObject->DeviceType, FILE_DEVICE_DISK_FILE_SYSTEM == FsctlDeviceObject->DeviceType ? 0 : FILE_REMOTE_DEVICE, &FsvolDeviceObject); if (!NT_SUCCESS(Result)) return Result; if (FILE_DEVICE_DISK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) { Result = FspDeviceCreateSecure(FspFsvrtDeviceExtensionKind, 0, &VolumeName, FILE_DEVICE_DISK, 0, &DeviceSddl, &FspFsvrtDeviceClassGuid, &FsvrtDeviceObject); if (!NT_SUCCESS(Result)) { FspDeviceDereference(FsvolDeviceObject); return Result; } #pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") FsvrtDeviceObject->SectorSize = VolumeParams.SectorSize; } else FsvrtDeviceObject = 0; #pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") FsvolDeviceObject->SectorSize = VolumeParams.SectorSize; FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); FsvolDeviceExtension->FsctlDeviceObject = FsctlDeviceObject; FsvolDeviceExtension->FsvrtDeviceObject = FsvrtDeviceObject; FsvolDeviceExtension->FsvolDeviceObject = FsvolDeviceObject; FsvolDeviceExtension->VolumeParams = VolumeParams; if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) RtlInitUnicodeString(&FsvolDeviceExtension->VolumePrefix, FsvolDeviceExtension->VolumeParams.Prefix); RtlInitEmptyUnicodeString(&FsvolDeviceExtension->VolumeName, FsvolDeviceExtension->VolumeNameBuf, sizeof FsvolDeviceExtension->VolumeNameBuf); RtlCopyUnicodeString(&FsvolDeviceExtension->VolumeName, &VolumeName); #if defined(FSP_CFG_REJECT_EARLY_IRP) if (!FsvolDeviceExtension->VolumeParams.RejectIrpPriorToTransact0) FsvolDeviceExtension->ReadyToAcceptIrp = 1; #endif Result = FspDeviceInitialize(FsvolDeviceObject); if (NT_SUCCESS(Result)) { if (0 != FsvrtDeviceObject) { FspFsvrtDeviceExtension(FsvrtDeviceObject)->SectorSize = FsvolDeviceExtension->VolumeParams.SectorSize; Result = FspDeviceInitialize(FsvrtDeviceObject); } } if (!NT_SUCCESS(Result)) { if (0 != FsvrtDeviceObject) FspDeviceDereference(FsvrtDeviceObject); FspDeviceDereference(FsvolDeviceObject); return Result; } /* do we need to register with fsmup? */ if (0 == FsvrtDeviceObject) { Result = FspMupRegister(Globals->FsmupDeviceObject, FsvolDeviceObject); if (!NT_SUCCESS(Result)) { FspDeviceDereference(FsvolDeviceObject); return Result; } RtlInitUnicodeString(&FsmupDeviceName, Globals->FsmupDeviceNameBuf); Result = IoCreateSymbolicLink(&FsvolDeviceExtension->VolumeName, &FsmupDeviceName); if (!NT_SUCCESS(Result)) { FspMupUnregister(Globals->FsmupDeviceObject, FsvolDeviceObject); FspDeviceDereference(FsvolDeviceObject); return Result; } } /* associate the new volume device with our file object */ FileObject->FsContext2 = FsvolDeviceObject; Irp->IoStatus.Information = FILE_OPENED; return STATUS_SUCCESS; } VOID FspVolumeDelete( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { // !PAGED_CODE(); FSP_SILO_GLOBALS *Globals; PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; FSP_FILE_NODE **FileNodes; ULONG FileNodeCount, Index; NTSTATUS Result; /* * If we have an fsvrt that is a mountdev, finalize it now! Finalizing a mountdev * involves interaction with the MountManager, which tries to open our devices. * So if we delay this interaction and we do it during final fsvrt teardown (i.e. * FspDeviceDelete time) we will fail such opens with STATUS_CANCELLED, which will * confuse the MountManager. */ if (0 != FsvrtDeviceObject) FspMountdevFini(FsvrtDeviceObject); FspDeviceReference(FsvolDeviceObject); FspSiloGetGlobals(&Globals); ASSERT(0 != Globals); FspDeviceGlobalLock(); FspVolumeDeleteNoLock(FsctlDeviceObject, Irp, IrpSp, Globals); FspDeviceGlobalUnlock(); FspSiloDereferenceGlobals(Globals); /* * Call MmForceSectionClosed on active files to ensure that Mm removes them from Standby List. */ Result = FspFileNodeCopyActiveList(FsvolDeviceObject, &FileNodes, &FileNodeCount); if (NT_SUCCESS(Result)) { for (Index = FileNodeCount - 1; FileNodeCount > Index; Index--) MmForceSectionClosed(&FileNodes[Index]->NonPaged->SectionObjectPointers, TRUE); FspFileNodeDeleteList(FileNodes, FileNodeCount); } FspDeviceDereference(FsvolDeviceObject); } static VOID FspVolumeDeleteNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, FSP_SILO_GLOBALS *Globals) { // !PAGED_CODE(); ASSERT(IRP_MJ_CLEANUP == IrpSp->MajorFunction); ASSERT(0 != IrpSp->FileObject->FsContext2); PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); IrpSp->FileObject->FsContext2 = 0; /* stop the I/O queue */ FspIoqStop(FsvolDeviceExtension->Ioq); /* do we have a virtual disk device or are we registered with fsmup? */ if (0 != FsvolDeviceExtension->FsvrtDeviceObject) { PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; PVPB OldVpb; KIRQL Irql; BOOLEAN DeleteVpb = FALSE; BOOLEAN DeleteDly = FALSE; LARGE_INTEGER Delay; /* swap the virtual disk device VPB with the preallocated one */ #pragma prefast(push) #pragma prefast(disable:28175, "We are a filesystem: ok to access Vpb") IoAcquireVpbSpinLock(&Irql); OldVpb = FsvrtDeviceObject->Vpb; if (0 != OldVpb) { FsvrtDeviceObject->Vpb = FsvolDeviceExtension->SwapVpb; FsvrtDeviceObject->Vpb->Size = sizeof *FsvrtDeviceObject->Vpb; FsvrtDeviceObject->Vpb->Type = IO_TYPE_VPB; FsvrtDeviceObject->Vpb->Flags = FlagOn(OldVpb->Flags, VPB_REMOVE_PENDING); FsvrtDeviceObject->Vpb->RealDevice = OldVpb->RealDevice; FsvrtDeviceObject->Vpb->RealDevice->Vpb = FsvrtDeviceObject->Vpb; DeleteVpb = 0 == OldVpb->ReferenceCount; DeleteDly = 2 <= OldVpb->ReferenceCount; } IoReleaseVpbSpinLock(Irql); #pragma prefast(pop) /* * Release the virtual disk object. This is safe to do here because the volume device * keeps an extra reference to the virtual disk object using ObReferenceObject. */ FspDeviceDereference(FsvrtDeviceObject); if (DeleteVpb) { /* no more references to the old VPB; delete now! */ FspFreeExternal(OldVpb); FsvolDeviceExtension->SwapVpb = 0; } else if (!DeleteDly) { /* there is only the reference from FspVolumeMount; release it! */ FspFreeExternal(OldVpb); FsvolDeviceExtension->SwapVpb = 0; FspDeviceDereference(FsvolDeviceObject); } else { /* VPB has extra references; we must do a delayed delete of the volume device */ FsvolDeviceExtension->SwapVpb = OldVpb; Delay.QuadPart = 300/*ms*/ * -10000; FspInitializeDelayedWorkItem(&FsvolDeviceExtension->DeleteVolumeDelayedWorkItem, FspVolumeDeleteDelayed, FsvolDeviceObject); FspQueueDelayedWorkItem(&FsvolDeviceExtension->DeleteVolumeDelayedWorkItem, Delay); } } else { IoDeleteSymbolicLink(&FsvolDeviceExtension->VolumeName); FspMupUnregister(Globals->FsmupDeviceObject, FsvolDeviceObject); } /* release the volume device object */ FspDeviceDereference(FsvolDeviceObject); } static VOID FspVolumeDeleteDelayed(PVOID Context) { // !PAGED_CODE(); PDEVICE_OBJECT FsvolDeviceObject = Context; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); KIRQL Irql; BOOLEAN DeleteVpb = FALSE; LARGE_INTEGER Delay; IoAcquireVpbSpinLock(&Irql); ASSERT(0 != FsvolDeviceExtension->SwapVpb->ReferenceCount); DeleteVpb = 1 == FsvolDeviceExtension->SwapVpb->ReferenceCount; if (DeleteVpb) FsvolDeviceExtension->SwapVpb->ReferenceCount = 0; IoReleaseVpbSpinLock(Irql); if (DeleteVpb) { FspDeviceGlobalLock(); FspFreeExternal(FsvolDeviceExtension->SwapVpb); FsvolDeviceExtension->SwapVpb = 0; FspDeviceDereference(FsvolDeviceObject); FspDeviceGlobalUnlock(); } else { Delay.QuadPart = 300/*ms*/ * -10000; FspQueueDelayedWorkItem(&FsvolDeviceExtension->DeleteVolumeDelayedWorkItem, Delay); } } NTSTATUS FspVolumeMount( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { // !PAGED_CODE(); NTSTATUS Result; FspDeviceGlobalLock(); Result = FspVolumeMountNoLock(FsctlDeviceObject, Irp, IrpSp); FspDeviceGlobalUnlock(); return Result; } static NTSTATUS FspVolumeMountNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { // !PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_MOUNT_VOLUME == IrpSp->MinorFunction); NTSTATUS Result; PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb; PDEVICE_OBJECT FsvrtDeviceObject = IrpSp->Parameters.MountVolume.DeviceObject; PDEVICE_OBJECT FsvolDeviceObject = 0; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = 0; PDEVICE_OBJECT *DeviceObjects = 0; ULONG DeviceObjectCount = 0; KIRQL Irql; /* check the passed in device object; it must be our own and not stopped */ Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount); if (NT_SUCCESS(Result)) { Result = STATUS_UNRECOGNIZED_VOLUME; for (ULONG i = 0; DeviceObjectCount > i; i++) if (FspDeviceReference(DeviceObjects[i])) { if (FspFsvolDeviceExtensionKind == FspDeviceExtension(DeviceObjects[i])->Kind) { FsvolDeviceObject = DeviceObjects[i]; FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); if (FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject) { if (!FspIoqStopped(FsvolDeviceExtension->Ioq)) { Result = STATUS_SUCCESS; /* break out of the loop without FspDeviceDereference */ break; } } } FspDeviceDereference(DeviceObjects[i]); } FspDeviceDeleteList(DeviceObjects, DeviceObjectCount); } if (!NT_SUCCESS(Result)) return Result; /* * At this point the volume device object we are going to use in the VPB * has been FspDeviceReference'd. * * We will increment the VPB's ReferenceCount so that we can do a delayed delete * of the volume device later on. */ ASSERT(0 != FsvolDeviceObject && 0 != FsvolDeviceExtension); IoAcquireVpbSpinLock(&Irql); Vpb->ReferenceCount++; Vpb->DeviceObject = FsvolDeviceObject; Vpb->SerialNumber = FsvolDeviceExtension->VolumeParams.VolumeSerialNumber; IoReleaseVpbSpinLock(Irql); /* * Argh! Turns out that the IrpSp->Parameters.MountVolume.DeviceObject is * passed to us with an extra reference, which is not removed on SUCCESS. * So go ahead and dereference it now! */ ObDereferenceObject(FsvrtDeviceObject); Irp->IoStatus.Information = 0; return STATUS_SUCCESS; } NTSTATUS FspVolumeMakeMountdev( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT(FSP_FSCTL_MOUNTDEV == IrpSp->Parameters.FileSystemControl.FsControlCode); ASSERT(0 != IrpSp->FileObject->FsContext2); PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; BOOLEAN Persistent = 0 < InputBufferLength ? !!*(PBOOLEAN)Irp->AssociatedIrp.SystemBuffer : FALSE; NTSTATUS Result; if (0 == FsvrtDeviceObject) return STATUS_INVALID_PARAMETER; if (sizeof(GUID) > OutputBufferLength) return STATUS_INVALID_PARAMETER; FspDeviceGlobalLock(); Result = FspMountdevMake(FsvrtDeviceObject, FsvolDeviceObject, Persistent); if (!NT_SUCCESS(Result)) { if (STATUS_TOO_LATE != Result) goto exit; } RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &FspFsvrtDeviceExtension(FsvrtDeviceObject)->UniqueId, sizeof(GUID)); Irp->IoStatus.Information = sizeof(GUID); Result = STATUS_SUCCESS; exit: FspDeviceGlobalUnlock(); return Result; } NTSTATUS FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT(FSP_FSCTL_VOLUME_NAME == IrpSp->Parameters.FileSystemControl.FsControlCode); ASSERT(0 != IrpSp->FileObject->FsContext2); /* check parameters */ ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; PVOID SystemBuffer = Irp->AssociatedIrp.SystemBuffer; if (FSP_FSCTL_VOLUME_NAME_SIZEMAX > OutputBufferLength) return STATUS_BUFFER_TOO_SMALL; PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); UNICODE_STRING VolumeName; ASSERT(FSP_FSCTL_VOLUME_NAME_SIZEMAX >= FsvolDeviceExtension->VolumeName.MaximumLength + FsvolDeviceExtension->VolumePrefix.MaximumLength + sizeof(WCHAR)); RtlInitEmptyUnicodeString(&VolumeName, SystemBuffer, FSP_FSCTL_VOLUME_NAME_SIZEMAX); RtlCopyUnicodeString(&VolumeName, &FsvolDeviceExtension->VolumeName); if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) RtlAppendUnicodeStringToString(&VolumeName, &FsvolDeviceExtension->VolumePrefix); VolumeName.Buffer[VolumeName.Length / sizeof(WCHAR)] = L'\0'; Irp->IoStatus.Information = VolumeName.Length + sizeof(WCHAR); return STATUS_SUCCESS; } NTSTATUS FspVolumeGetNameList( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); NTSTATUS Result; FspDeviceGlobalLock(); Result = FspVolumeGetNameListNoLock(FsctlDeviceObject, Irp, IrpSp); FspDeviceGlobalUnlock(); return Result; } static NTSTATUS FspVolumeGetNameListNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT(FSP_FSCTL_VOLUME_LIST == IrpSp->Parameters.FileSystemControl.FsControlCode); NTSTATUS Result; ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; PVOID SystemBuffer = Irp->AssociatedIrp.SystemBuffer; PDEVICE_OBJECT *DeviceObjects = 0; ULONG DeviceObjectCount = 0; PDEVICE_OBJECT FsvolDeviceObject; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; UNICODE_STRING VolumeList; USHORT Length; if (65535/*USHRT_MAX*/ < OutputBufferLength) return STATUS_INVALID_PARAMETER; Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount); if (!NT_SUCCESS(Result)) return Result; Result = STATUS_SUCCESS; RtlInitEmptyUnicodeString(&VolumeList, SystemBuffer, (USHORT)OutputBufferLength); for (ULONG i = 0; NT_SUCCESS(Result) && DeviceObjectCount > i; i++) if (FspDeviceReference(DeviceObjects[i])) { if (FspFsvolDeviceExtensionKind == FspDeviceExtension(DeviceObjects[i])->Kind) { FsvolDeviceObject = DeviceObjects[i]; FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); if (FsvolDeviceExtension->FsctlDeviceObject == FsctlDeviceObject && !FspIoqStopped(FsvolDeviceExtension->Ioq)) { Length = FsvolDeviceExtension->VolumeName.Length + FsvolDeviceExtension->VolumePrefix.Length + sizeof(WCHAR); if (VolumeList.Length + Length <= VolumeList.MaximumLength) { RtlAppendUnicodeStringToString(&VolumeList, &FsvolDeviceExtension->VolumeName); if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) RtlAppendUnicodeStringToString(&VolumeList, &FsvolDeviceExtension->VolumePrefix); VolumeList.Buffer[VolumeList.Length / sizeof(WCHAR)] = L'\0'; VolumeList.Length += sizeof(WCHAR); } else { VolumeList.Length = 0; Result = STATUS_BUFFER_TOO_SMALL; } } } FspDeviceDereference(DeviceObjects[i]); } FspDeviceDeleteList(DeviceObjects, DeviceObjectCount); Irp->IoStatus.Information = VolumeList.Length; return Result; } NTSTATUS FspVolumeTransact( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT( FSP_FSCTL_TRANSACT == IrpSp->Parameters.FileSystemControl.FsControlCode || FSP_FSCTL_TRANSACT_BATCH == IrpSp->Parameters.FileSystemControl.FsControlCode || FSP_FSCTL_TRANSACT_INTERNAL == IrpSp->Parameters.FileSystemControl.FsControlCode); ASSERT(0 != IrpSp->FileObject->FsContext2); /* check parameters */ PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; ULONG ControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; PVOID InputBuffer = 0; PVOID OutputBuffer = 0; if (FSP_FSCTL_TRANSACT_INTERNAL == ControlCode) { InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; if (KernelMode != Irp->RequestorMode) return STATUS_INVALID_DEVICE_REQUEST; ASSERT(0 == InputBufferLength || FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof(FSP_FSCTL_TRANSACT_RSP)) <= InputBufferLength); ASSERT(0 == OutputBufferLength || sizeof(PVOID) <= OutputBufferLength); } else { InputBuffer = Irp->AssociatedIrp.SystemBuffer; if (0 != InputBufferLength && FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof(FSP_FSCTL_TRANSACT_RSP)) > InputBufferLength) return STATUS_INVALID_PARAMETER; if (0 != OutputBufferLength && ((FSP_FSCTL_TRANSACT == ControlCode && FSP_FSCTL_TRANSACT_BUFFER_SIZEMIN > OutputBufferLength) || (FSP_FSCTL_TRANSACT_BATCH == ControlCode && FSP_FSCTL_TRANSACT_BATCH_BUFFER_SIZEMIN > OutputBufferLength))) return STATUS_BUFFER_TOO_SMALL; } if (!FspDeviceReference(FsvolDeviceObject)) return STATUS_CANCELLED; #if defined(FSP_CFG_REJECT_EARLY_IRP) if (0 == InputBufferLength && 0 == OutputBufferLength) FspFsvolDeviceSetReadyToAcceptIrp(FsvolDeviceObject); #endif NTSTATUS Result; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PUINT8 BufferEnd; FSP_FSCTL_TRANSACT_RSP *Response, *NextResponse; FSP_FSCTL_TRANSACT_REQ *Request, *PendingIrpRequest; PVOID InternalBuffer = 0; PIRP ProcessIrp, PendingIrp, RetriedIrp, RepostedIrp; ULONG LoopCount; LARGE_INTEGER Timeout; PIRP TopLevelIrp = IoGetTopLevelIrp(); /* process any user-mode file system responses */ RepostedIrp = 0; Response = InputBuffer; BufferEnd = (PUINT8)InputBuffer + InputBufferLength; for (;;) { NextResponse = FspFsctlTransactConsumeResponse(Response, BufferEnd); if (0 == NextResponse) break; ProcessIrp = FspIoqEndProcessingIrp(FsvolDeviceExtension->Ioq, (UINT_PTR)Response->Hint); if (0 == ProcessIrp) { /* either IRP was canceled or a bogus Hint was provided */ DEBUGLOG("BOGUS(Kind=%d, Hint=%p)", Response->Kind, (PVOID)(UINT_PTR)Response->Hint); Response = NextResponse; continue; } ASSERT((UINT_PTR)ProcessIrp == (UINT_PTR)Response->Hint); ASSERT(FspIrpRequest(ProcessIrp)->Hint == Response->Hint); IoSetTopLevelIrp(ProcessIrp); Result = FspIopDispatchComplete(ProcessIrp, Response); if (STATUS_PENDING == Result) { /* * The IRP has been reposted to our Ioq. Remember the first such IRP, * so that we know to break the loop if we see it again. */ if (0 == RepostedIrp) RepostedIrp = ProcessIrp; } Response = NextResponse; } /* process any retried IRP's */ LoopCount = FspIoqRetriedIrpCount(FsvolDeviceExtension->Ioq); while (0 < LoopCount--) /* upper bound on loop guarantees forward progress! */ { /* get the next retried IRP, but do not go beyond the first reposted IRP! */ RetriedIrp = FspIoqNextCompleteIrp(FsvolDeviceExtension->Ioq, RepostedIrp); if (0 == RetriedIrp) break; IoSetTopLevelIrp(RetriedIrp); Response = FspIopIrpResponse(RetriedIrp); Result = FspIopDispatchComplete(RetriedIrp, Response); if (STATUS_PENDING == Result) { /* * The IRP has been reposted to our Ioq. Remember the first such IRP, * so that we know to break the loop if we see it again. */ if (0 == RepostedIrp) RepostedIrp = RetriedIrp; } } /* were we sent an output buffer? */ switch (ControlCode & 3) { case METHOD_NEITHER: OutputBuffer = Irp->UserBuffer; break; case METHOD_OUT_DIRECT: if (0 != Irp->MdlAddress) OutputBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); break; case METHOD_BUFFERED: if (0 != OutputBufferLength) OutputBuffer = Irp->AssociatedIrp.SystemBuffer; break; default: ASSERT(0); break; } if (0 == OutputBuffer) { Irp->IoStatus.Information = 0; Result = STATUS_SUCCESS; goto exit; } /* wait for an IRP to arrive */ KeQuerySystemTime(&Timeout); Timeout.QuadPart += FsvolDeviceExtension->VolumeParams.TransactTimeout * 10000ULL; /* convert millis to nanos and add to absolute time */ if (0 == (PendingIrp = FspIoqNextPendingIrp(FsvolDeviceExtension->Ioq, 0, &Timeout, Irp))) { if (FspIoqStopped(FsvolDeviceExtension->Ioq)) { Result = STATUS_CANCELLED; goto exit; } PendingIrp = FspIoqTimeout; } if (FspIoqTimeout == PendingIrp || FspIoqCancelled == PendingIrp) { Irp->IoStatus.Information = 0; Result = FspIoqTimeout == PendingIrp ? STATUS_SUCCESS : STATUS_CANCELLED; goto exit; } /* send any pending IRP's to the user-mode file system */ RepostedIrp = 0; Request = OutputBuffer; BufferEnd = (PUINT8)OutputBuffer + OutputBufferLength; ASSERT(FSP_FSCTL_TRANSACT_INTERNAL == ControlCode ? TRUE : FspFsctlTransactCanProduceRequest(Request, BufferEnd)); LoopCount = FspIoqPendingIrpCount(FsvolDeviceExtension->Ioq); for (;;) { PendingIrpRequest = FspIrpRequest(PendingIrp); IoSetTopLevelIrp(PendingIrp); Result = FspIopDispatchPrepare(PendingIrp, PendingIrpRequest); if (STATUS_PENDING == Result) { /* * The IRP has been reposted to our Ioq. Remember the first such IRP, * so that we know to break the loop if we see it again. */ if (0 == RepostedIrp) RepostedIrp = PendingIrp; } else if (!NT_SUCCESS(Result)) FspIopCompleteIrp(PendingIrp, Result); else { if (FSP_FSCTL_TRANSACT_INTERNAL == ControlCode) { InternalBuffer = FspAllocatePoolMustSucceed( PagedPool, PendingIrpRequest->Size, FSP_ALLOC_EXTERNAL_TAG); RtlCopyMemory(InternalBuffer, PendingIrpRequest, PendingIrpRequest->Size); *(PVOID *)OutputBuffer = InternalBuffer; } else { RtlCopyMemory(Request, PendingIrpRequest, PendingIrpRequest->Size); Request = FspFsctlTransactProduceRequest(Request, PendingIrpRequest->Size); } if (!FspIoqStartProcessingIrp(FsvolDeviceExtension->Ioq, PendingIrp)) { /* * This can only happen if the Ioq was stopped. Abandon everything * and return STATUS_CANCELLED. Any IRP's in the Pending and Process * queues of the Ioq will be cancelled during FspIoqStop(). We must * also cancel the PendingIrp we have in our hands. */ ASSERT(FspIoqStopped(FsvolDeviceExtension->Ioq)); if (0 != InternalBuffer) { ASSERT(FSP_FSCTL_TRANSACT_INTERNAL == ControlCode); *(PVOID *)OutputBuffer = 0; FspFree(InternalBuffer); } FspIopCompleteCanceledIrp(PendingIrp); Result = STATUS_CANCELLED; goto exit; } /* are we doing single request or batch mode? */ if (FSP_FSCTL_TRANSACT_INTERNAL == ControlCode) { Irp->IoStatus.Information = sizeof(PVOID); Result = STATUS_SUCCESS; goto exit; } else if (FSP_FSCTL_TRANSACT == ControlCode) break; /* check that we have enough space before pulling the next pending IRP off the queue */ if (!FspFsctlTransactCanProduceRequest(Request, BufferEnd)) break; } if (0 >= LoopCount--) /* upper bound on loop guarantees forward progress! */ break; /* get the next pending IRP, but do not go beyond the first reposted IRP! */ PendingIrp = FspIoqNextPendingIrp(FsvolDeviceExtension->Ioq, RepostedIrp, 0, Irp); if (0 == PendingIrp) break; } Irp->IoStatus.Information = (PUINT8)Request - (PUINT8)OutputBuffer; Result = STATUS_SUCCESS; exit: IoSetTopLevelIrp(TopLevelIrp); FspDeviceDereference(FsvolDeviceObject); return Result; } NTSTATUS FspVolumeTransactFsext( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT(CTL_CODE(0, 0xC00, 0, 0) == (IrpSp->Parameters.FileSystemControl.FsControlCode & CTL_CODE(0, 0xC00, 0, 0))); ASSERT(0 != IrpSp->FileObject->FsContext2); PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; if (!FspDeviceReference(FsvolDeviceObject)) return STATUS_CANCELLED; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST; if (IrpSp->Parameters.FileSystemControl.FsControlCode == FsvolDeviceExtension->VolumeParams.FsextControlCode) { if (0 != FsvolDeviceExtension->Provider) Result = FsvolDeviceExtension->Provider->DeviceTransact(FsvolDeviceObject, Irp); } FspDeviceDereference(FsvolDeviceObject); return Result; } NTSTATUS FspVolumeStop( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT(FSP_FSCTL_STOP == IrpSp->Parameters.FileSystemControl.FsControlCode); ASSERT(0 != IrpSp->FileObject->FsContext2); PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); FspIoqStop(FsvolDeviceExtension->Ioq); return STATUS_SUCCESS; } typedef struct { PDEVICE_OBJECT FsvolDeviceObject; PVOID InputBuffer; ULONG InputBufferLength; WORK_QUEUE_ITEM WorkItem; } FSP_VOLUME_NOTIFY_WORK_ITEM; NTSTATUS FspVolumeNotify( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); /* * FspVolumeNotify processing requires multiple locks that cannot be acquired * synchronously or deadlocks are possible. (The reason is that FspVolumeNotify * may be called by the user mode file system while servicing a request that * has already acquired one of the required locks.) * * For this reason FspVolumeNotify does its processing asynchronously; it ships * its payload as a work item to a system worker thread, which will perform the * actual processing. See FspVolumeNotifyWork. */ ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT(FSP_FSCTL_NOTIFY == IrpSp->Parameters.FileSystemControl.FsControlCode); ASSERT(METHOD_NEITHER == (IrpSp->Parameters.FileSystemControl.FsControlCode & 3)); ASSERT(0 != IrpSp->FileObject->FsContext2); PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; PVOID InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = 0; NTSTATUS Result; if (!FspDeviceReference(FsvolDeviceObject)) return STATUS_CANCELLED; NotifyWorkItem = FspAllocNonPaged(sizeof *NotifyWorkItem); if (0 == NotifyWorkItem) { Result = STATUS_INSUFFICIENT_RESOURCES; goto fail; } NotifyWorkItem->FsvolDeviceObject = FsvolDeviceObject; NotifyWorkItem->InputBuffer = FspAllocNonPaged(InputBufferLength); if (0 == NotifyWorkItem->InputBuffer) { Result = STATUS_INSUFFICIENT_RESOURCES; goto fail; } try { ProbeForRead(InputBuffer, InputBufferLength, 1); RtlCopyMemory(NotifyWorkItem->InputBuffer, InputBuffer, InputBufferLength); NotifyWorkItem->InputBufferLength = InputBufferLength; } except (EXCEPTION_EXECUTE_HANDLER) { Result = GetExceptionCode(); Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; goto fail; } ExInitializeWorkItem(&NotifyWorkItem->WorkItem, FspVolumeNotifyWork, NotifyWorkItem); ExQueueWorkItem(&NotifyWorkItem->WorkItem, DelayedWorkQueue); return STATUS_SUCCESS; fail: if (0 != NotifyWorkItem) { if (0 != NotifyWorkItem->InputBuffer) FspFree(NotifyWorkItem->InputBuffer); FspFree(NotifyWorkItem); } FspDeviceDereference(FsvolDeviceObject); return Result; } static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0) { PAGED_CODE(); FsRtlEnterFileSystem(); IoSetTopLevelIrp(0); FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = NotifyWorkItem0; PDEVICE_OBJECT FsvolDeviceObject = NotifyWorkItem->FsvolDeviceObject; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); FSP_FSCTL_NOTIFY_INFO *NotifyInfo = NotifyWorkItem->InputBuffer; PUINT8 NotifyInfoEnd = (PUINT8)NotifyInfo + NotifyWorkItem->InputBufferLength; ULONG NotifyInfoSize; UNICODE_STRING FileName = { 0 }, StreamPart = { 0 }, AbsFileName = { 0 }, FullFileName = { 0 }; ULONG StreamType = FspFileNameStreamTypeNone; NTSTATUS Result; /* * Acquire the rename lock shared to disallow concurrent RENAME's. * * This guards against the race where a file that we want to invalidate * is being concurrently renamed to a different name. Thus we may think * that the file is not open and not invalidate its caches, whereas the * file has simply changed name. */ FspFsvolDeviceFileRenameAcquireShared(FsvolDeviceObject); /* iterate over notify information and invalidate/notify each file */ for (; (PUINT8)NotifyInfo + sizeof(NotifyInfo->Size) <= NotifyInfoEnd; NotifyInfo = (PVOID)((PUINT8)NotifyInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(NotifyInfoSize))) { NotifyInfoSize = NotifyInfo->Size; if (sizeof(FSP_FSCTL_NOTIFY_INFO) > NotifyInfoSize) break; FileName.Length = FileName.MaximumLength = (USHORT)(NotifyInfoSize - sizeof(FSP_FSCTL_NOTIFY_INFO)); FileName.Buffer = NotifyInfo->FileNameBuf; if (!FspFileNameIsValid(&FileName, FsvolDeviceExtension->VolumeParams.MaxComponentLength, FsvolDeviceExtension->VolumeParams.NamedStreams ? &StreamPart : 0, &StreamType)) continue; if (sizeof(WCHAR) <= FileName.Length && L'\\' == FileName.Buffer[0]) { /* absolute file names are used as-is */ AbsFileName = FileName; FspFileNodeInvalidateCachesAndNotifyChangeByName(FsvolDeviceObject, &FileName, NotifyInfo->Filter, NotifyInfo->Action, TRUE); } else if (0 != AbsFileName.Length) { /* relative file names are considered relative to the last absolute file name */ if (0 == FullFileName.Buffer) { FullFileName.Buffer = FspAllocatePoolMustSucceed( NonPagedPool, FSP_FSCTL_TRANSACT_PATH_SIZEMAX, FSP_ALLOC_INTERNAL_TAG); FullFileName.MaximumLength = FSP_FSCTL_TRANSACT_PATH_SIZEMAX; } FullFileName.Length = 0; Result = RtlAppendUnicodeStringToString(&FullFileName, &AbsFileName); if (NT_SUCCESS(Result)) { if (sizeof(WCHAR) * 2/* not empty or root */ <= AbsFileName.Length && L'\\' == AbsFileName.Buffer[AbsFileName.Length / sizeof(WCHAR) - 1]) Result = RtlAppendUnicodeToString(&FullFileName, L"\\"); } if (NT_SUCCESS(Result)) Result = RtlAppendUnicodeStringToString(&FullFileName, &FileName); if (NT_SUCCESS(Result)) FspFileNodeInvalidateCachesAndNotifyChangeByName(FsvolDeviceObject, &FullFileName, NotifyInfo->Filter, NotifyInfo->Action, FALSE); } } FspFsvolDeviceFileRenameRelease(FsvolDeviceObject); if (0 != FullFileName.Buffer) FspFree(FullFileName.Buffer); FspFree(NotifyWorkItem->InputBuffer); FspFree(NotifyWorkItem); FspDeviceDereference(FsvolDeviceObject); FsRtlExitFileSystem(); } NTSTATUS FspVolumeWork( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); ASSERT( FSP_FSCTL_WORK == IrpSp->Parameters.FileSystemControl.FsControlCode || FSP_FSCTL_WORK_BEST_EFFORT == IrpSp->Parameters.FileSystemControl.FsControlCode); if (KernelMode != Irp->RequestorMode) return STATUS_INVALID_DEVICE_REQUEST; NTSTATUS Result; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); FSP_FSCTL_TRANSACT_REQ *Request = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; BOOLEAN BestEffort = FSP_FSCTL_WORK_BEST_EFFORT == IrpSp->Parameters.FileSystemControl.FsControlCode; ASSERT(0 == Request->Hint); /* associate the passed Request with our Irp; acquire ownership of the Request */ Request->Hint = (UINT_PTR)Irp; FspIrpSetRequest(Irp, Request); /* * Post the IRP to our Ioq; we do this here instead of at FSP_LEAVE_MJ time, * so that we can disassociate the Request on failure and release ownership * back to the caller. */ if (!FspIoqPostIrpEx(FsvolDeviceExtension->Ioq, Irp, BestEffort, &Result)) { Request->Hint = 0; FspIrpSetRequest(Irp, 0); } DEBUGLOG("%s(Irp=%p) = %s", IoctlCodeSym(BestEffort ? FSP_FSCTL_WORK_BEST_EFFORT : FSP_FSCTL_WORK), Irp, /* referencing pointer value, which is safe despite FspIoqPostIrpEx above! */ NtStatusSym(Result)); return Result; }