diff options
| author | Dave Wilson <[email protected]> | 2015-04-29 09:48:49 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-04-29 10:47:29 -0700 |
| commit | d40232638faaab19de557e70a3ee938da7a35295 (patch) | |
| tree | 14e61c973745949af186b7940e3a53d25322f5de /filesys | |
| parent | 02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff) | |
samples update for //build
Diffstat (limited to 'filesys')
74 files changed, 19222 insertions, 276 deletions
diff --git a/filesys/cdfs/cddata.c b/filesys/cdfs/cddata.c index 19648e36..15cfe2d5 100644 --- a/filesys/cdfs/cddata.c +++ b/filesys/cdfs/cddata.c @@ -194,7 +194,22 @@ CDFS_TELEMETRY_DATA_CONTEXT CdTelemetryData; #pragma alloc_text(PAGE, CdSetThreadContext) #endif - +_IRQL_requires_max_(APC_LEVEL) +__drv_dispatchType(DRIVER_DISPATCH) +__drv_dispatchType(IRP_MJ_CREATE) +__drv_dispatchType(IRP_MJ_CLOSE) +__drv_dispatchType(IRP_MJ_READ) +__drv_dispatchType(IRP_MJ_WRITE) +__drv_dispatchType(IRP_MJ_QUERY_INFORMATION) +__drv_dispatchType(IRP_MJ_SET_INFORMATION) +__drv_dispatchType(IRP_MJ_QUERY_VOLUME_INFORMATION) +__drv_dispatchType(IRP_MJ_DIRECTORY_CONTROL) +__drv_dispatchType(IRP_MJ_FILE_SYSTEM_CONTROL) +__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) +__drv_dispatchType(IRP_MJ_LOCK_CONTROL) +__drv_dispatchType(IRP_MJ_CLEANUP) +__drv_dispatchType(IRP_MJ_PNP) +__drv_dispatchType(IRP_MJ_SHUTDOWN) NTSTATUS CdFsdDispatch ( _In_ PDEVICE_OBJECT DeviceObject, diff --git a/filesys/cdfs/cdfs.sln b/filesys/cdfs/cdfs.sln index 1dbf3f5c..b45ac8df 100644 --- a/filesys/cdfs/cdfs.sln +++ b/filesys/cdfs/cdfs.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdfs", "cdfs.vcxproj", "{CF3DA8BB-C366-42EF-B62B-F3D799F94B72}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdfs", "cdfs.vcxproj", "{6E1BC036-32B6-4CDA-A44E-13471EF68E06}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|Win32.ActiveCfg = Debug|Win32 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|Win32.Build.0 = Debug|Win32 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|Win32.ActiveCfg = Release|Win32 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|Win32.Build.0 = Release|Win32 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|x64.ActiveCfg = Debug|x64 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|x64.Build.0 = Debug|x64 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|x64.ActiveCfg = Release|x64 - {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|x64.Build.0 = Release|x64 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|Win32.ActiveCfg = Debug|Win32 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|Win32.Build.0 = Debug|Win32 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|Win32.ActiveCfg = Release|Win32 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|Win32.Build.0 = Release|Win32 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|x64.ActiveCfg = Debug|x64 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|x64.Build.0 = Debug|x64 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|x64.ActiveCfg = Release|x64 + {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/cdfs/cdfs.vcxproj b/filesys/cdfs/cdfs.vcxproj index 8e87f675..4c0cae16 100644 --- a/filesys/cdfs/cdfs.vcxproj +++ b/filesys/cdfs/cdfs.vcxproj @@ -19,12 +19,12 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{CF3DA8BB-C366-42EF-B62B-F3D799F94B72}</ProjectGuid> + <ProjectGuid>{6E1BC036-32B6-4CDA-A44E-13471EF68E06}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <SupportsPackaging>false</SupportsPackaging> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{1340E057-CDDF-46D6-B5D9-394AF1B2CA07}</SampleGuid> + <SampleGuid>{B7974984-CC80-44F6-A5FF-3450139B650B}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/cdfs/cdfs.vcxproj.Filters b/filesys/cdfs/cdfs.vcxproj.Filters index e5978b9a..f9071fe0 100644 --- a/filesys/cdfs/cdfs.vcxproj.Filters +++ b/filesys/cdfs/cdfs.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{6041F7C5-67BE-4EBD-A1B8-53A22C207899}</UniqueIdentifier> + <UniqueIdentifier>{DDD1C6BA-4FDB-4B31-8CA1-A98A3B87BCAD}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{7B185A7C-1201-4E8B-80F4-79EC615CB7F0}</UniqueIdentifier> + <UniqueIdentifier>{6330F9B6-65BA-4A38-955C-1968DCB91EDE}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{2979BE05-350C-4B63-96FF-B319CCBD776F}</UniqueIdentifier> + <UniqueIdentifier>{D575D231-5B8E-4A00-BBEF-7105E4F2B0FB}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{217A878D-9867-4135-9B60-4809FBE6E1CE}</UniqueIdentifier> + <UniqueIdentifier>{B1FE81D8-D0AC-4BB6-B311-CF9DA3709742}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/cdfs/cdprocs.h b/filesys/cdfs/cdprocs.h index 6645e539..d2f7db0b 100644 --- a/filesys/cdfs/cdprocs.h +++ b/filesys/cdfs/cdprocs.h @@ -1707,13 +1707,29 @@ CdLbnToMmSsFf ( // contained in CdData.c // -// NTSTATUS -// CdFsdDispatch ( -// _In_ PDEVICE_OBJECT DeviceObject, -// _Inout_ PIRP Irp -// ); +_IRQL_requires_max_(APC_LEVEL) +__drv_dispatchType(DRIVER_DISPATCH) +__drv_dispatchType(IRP_MJ_CREATE) +__drv_dispatchType(IRP_MJ_CLOSE) +__drv_dispatchType(IRP_MJ_READ) +__drv_dispatchType(IRP_MJ_WRITE) +__drv_dispatchType(IRP_MJ_QUERY_INFORMATION) +__drv_dispatchType(IRP_MJ_SET_INFORMATION) +__drv_dispatchType(IRP_MJ_QUERY_VOLUME_INFORMATION) +__drv_dispatchType(IRP_MJ_DIRECTORY_CONTROL) +__drv_dispatchType(IRP_MJ_FILE_SYSTEM_CONTROL) +__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) +__drv_dispatchType(IRP_MJ_LOCK_CONTROL) +__drv_dispatchType(IRP_MJ_CLEANUP) +__drv_dispatchType(IRP_MJ_PNP) +__drv_dispatchType(IRP_MJ_SHUTDOWN) +NTSTATUS +CdFsdDispatch ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ); -DRIVER_DISPATCH CdFsdDispatch; +// DRIVER_DISPATCH CdFsdDispatch; LONG CdExceptionFilter ( diff --git a/filesys/cdfs/deviosup.c b/filesys/cdfs/deviosup.c index a6a69c64..b2f9467b 100644 --- a/filesys/cdfs/deviosup.c +++ b/filesys/cdfs/deviosup.c @@ -3544,7 +3544,9 @@ Return Value: PCD_IO_CONTEXT IoContext = Context; _Analysis_assume_(Context != NULL); AssertVerifyDeviceIrp( Irp ); - + + UNREFERENCED_PARAMETER( DeviceObject ); + // // If we got an error (or verify required), remember it in the Irp // @@ -3614,7 +3616,6 @@ Return Value: return STATUS_MORE_PROCESSING_REQUIRED; } - UNREFERENCED_PARAMETER( DeviceObject ); } @@ -3718,6 +3719,8 @@ Return Value: { PCD_IO_CONTEXT IoContext = Context; + UNREFERENCED_PARAMETER( DeviceObject ); + _Analysis_assume_(IoContext != NULL); AssertVerifyDeviceIrp( Irp ); @@ -3742,6 +3745,7 @@ Return Value: // Now release the resource // + _Analysis_assume_lock_held_(*IoContext->Resource); ExReleaseResourceForThreadLite( IoContext->Resource, IoContext->ResourceThreadId ); // @@ -3751,7 +3755,6 @@ Return Value: CdFreeIoContext( IoContext ); return STATUS_SUCCESS; - UNREFERENCED_PARAMETER( DeviceObject ); } diff --git a/filesys/miniFilter/MetadataManager/MetadataManager.sln b/filesys/miniFilter/MetadataManager/MetadataManager.sln index 9af2fc43..6f4e3cb4 100644 --- a/filesys/miniFilter/MetadataManager/MetadataManager.sln +++ b/filesys/miniFilter/MetadataManager/MetadataManager.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmm", "fmm.vcxproj", "{A95B7D4F-B926-4E1F-A051-E66091E08D3A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmm", "fmm.vcxproj", "{D7BF32EC-9C86-43C5-B512-9D40E9617518}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|Win32.ActiveCfg = Debug|Win32 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|Win32.Build.0 = Debug|Win32 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|Win32.ActiveCfg = Release|Win32 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|Win32.Build.0 = Release|Win32 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|x64.ActiveCfg = Debug|x64 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|x64.Build.0 = Debug|x64 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|x64.ActiveCfg = Release|x64 - {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|x64.Build.0 = Release|x64 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|Win32.ActiveCfg = Debug|Win32 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|Win32.Build.0 = Debug|Win32 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|Win32.ActiveCfg = Release|Win32 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|Win32.Build.0 = Release|Win32 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|x64.ActiveCfg = Debug|x64 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|x64.Build.0 = Debug|x64 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|x64.ActiveCfg = Release|x64 + {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj b/filesys/miniFilter/MetadataManager/fmm.vcxproj index c2da0192..08369fc5 100644 --- a/filesys/miniFilter/MetadataManager/fmm.vcxproj +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{A95B7D4F-B926-4E1F-A051-E66091E08D3A}</ProjectGuid> + <ProjectGuid>{D7BF32EC-9C86-43C5-B512-9D40E9617518}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{DD166882-E368-4576-B537-05B15D55B977}</SampleGuid> + <SampleGuid>{7C86E1A6-1B03-4023-BC29-22867C198C7F}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters index b76f8eb2..49f80878 100644 --- a/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{3AEFB0E9-101A-43AB-8E44-D384CB8D17F8}</UniqueIdentifier> + <UniqueIdentifier>{50875A86-C80E-4F00-B5E6-F8750B4A63D5}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{58BAA0C3-0CF4-438F-A011-53C6310A7E5E}</UniqueIdentifier> + <UniqueIdentifier>{47046E6F-7445-430F-9F9F-33D5F2C15DDA}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{168C91BC-EA8F-4E36-9315-77ACF64E6FA7}</UniqueIdentifier> + <UniqueIdentifier>{392DBF7C-4449-4E3A-9354-2F7AC7DCDE78}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{95E307D5-2257-4A2D-AA47-58C291F7AF68}</UniqueIdentifier> + <UniqueIdentifier>{E94FBEAE-1CA4-4F77-93A5-934B5212B838}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/NameChanger/NameChanger.inf b/filesys/miniFilter/NameChanger/NameChanger.inf new file mode 100644 index 00000000..a79016a6 --- /dev/null +++ b/filesys/miniFilter/NameChanger/NameChanger.inf @@ -0,0 +1,105 @@ +;;; +;;; NameChanger +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/05/2009,1.0.0.1 +CatalogFile = NameChanger.cat + +[SourceDisksNames] +12=%ServiceDescription%,,, + +[SourceDisksFiles] +NameChanger.sys = 12 + + +[DestinationDirs] +DefaultDestDir = 12 +NameChanger.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = NameChanger.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,NameChanger.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = NameChanger.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[NameChanger.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = NameChanger.AddRegistry + +; +; Registry Modifications +; + +[NameChanger.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,,"UserMapping",0x00000000,%UserMapping% +HKR,,"UserMappingFinalComponentShort",0x00000000,%UserMappingFinalComponentShort% +HKR,,"RealMapping",0x00000000,%RealMapping% + +; +; Copy Files +; + +[NameChanger.DriverFiles] +%DriverName%.sys + + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "NameChanger Mini-Filter Driver Sample" +ServiceName = "NameChanger" +DriverName = "NameChanger" + +;Instances specific information. +DefaultInstance = "NameChanger Instance" +Instance1.Name = "NameChanger Instance" +Instance1.Altitude = "370120" +Instance1.Flags = 0x0 ; Allow all attachments + +;Default settings +UserMapping = "\X\Y" +UserMappingFinalComponentShort = "Y" +RealMapping = "\A\B" + diff --git a/filesys/miniFilter/NameChanger/NameChanger.sln b/filesys/miniFilter/NameChanger/NameChanger.sln new file mode 100644 index 00000000..f4ae9420 --- /dev/null +++ b/filesys/miniFilter/NameChanger/NameChanger.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NameChanger", "NameChanger.vcxproj", "{5CA32B6F-182F-4028-837A-041E30696D95}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|Win32.ActiveCfg = Debug|Win32 + {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|Win32.Build.0 = Debug|Win32 + {5CA32B6F-182F-4028-837A-041E30696D95}.Release|Win32.ActiveCfg = Release|Win32 + {5CA32B6F-182F-4028-837A-041E30696D95}.Release|Win32.Build.0 = Release|Win32 + {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|x64.ActiveCfg = Debug|x64 + {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|x64.Build.0 = Debug|x64 + {5CA32B6F-182F-4028-837A-041E30696D95}.Release|x64.ActiveCfg = Release|x64 + {5CA32B6F-182F-4028-837A-041E30696D95}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/NameChanger/NameChanger.vcxproj b/filesys/miniFilter/NameChanger/NameChanger.vcxproj new file mode 100644 index 00000000..5b088fcb --- /dev/null +++ b/filesys/miniFilter/NameChanger/NameChanger.vcxproj @@ -0,0 +1,205 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{5CA32B6F-182F-4028-837A-041E30696D95}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{505C6DEB-79DC-43DC-AE00-5A573EF44152}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>NameChanger</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>NameChanger</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>NameChanger</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>NameChanger</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="nc.c" /> + <ClCompile Include="nccompat.c" /> + <ClCompile Include="nccontext.c" /> + <ClCompile Include="nccreate.c" /> + <ClCompile Include="ncdirenum.c" /> + <ClCompile Include="ncdirnotify.c" /> + <ClCompile Include="ncfileinfo.c" /> + <ClCompile Include="ncfsctrl.c" /> + <ClCompile Include="nchelper.c" /> + <ClCompile Include="ncinit.c" /> + <ClCompile Include="ncmapping.c" /> + <ClCompile Include="ncnameprov.c" /> + <ClCompile Include="ncoffsets.c" /> + <ClCompile Include="ncpath.c" /> + <ResourceCompile Include="nc.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters b/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters new file mode 100644 index 00000000..6520d305 --- /dev/null +++ b/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{817F7394-22D9-49B6-A7EE-2CC524F1CBF2}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{DF2CCD0F-541A-4EFC-AA37-579066F38CD0}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{86478AE0-8DEC-4E96-924F-3D6DA78E254F}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{D01D724E-D3A3-464D-8307-CE7E057FA327}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="nc.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="nccompat.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="nccontext.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="nccreate.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncdirenum.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncdirnotify.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncfileinfo.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncfsctrl.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="nchelper.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncinit.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncmapping.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncnameprov.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncoffsets.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ncpath.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="nc.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/NameChanger/ReadMe.md b/filesys/miniFilter/NameChanger/ReadMe.md new file mode 100644 index 00000000..9846414b --- /dev/null +++ b/filesys/miniFilter/NameChanger/ReadMe.md @@ -0,0 +1,116 @@ +NameChanger File System Minifilter Driver +========================================= + +The *NameChanger* minifilter grafts a directory from one part of a volume's namespace to another part using a mapping. The minifilter maintains this illusion by acting as a name provider, injecting entries into directory enumerations and forwarding directory change notifications. + + +Build the sample +---------------- + +You can build the sample in two ways: using Microsoft Visual Studio or the command line (*MSBuild*). + +Building a Driver Using Visual Studio +------------------------------------- + +You build a driver the same way you build any project or solution in Visual Studio. When you create a new driver project using a Windows driver template, the template defines a default (active) project configuration and a default (active) solution build configuration. When you create a project from existing driver sources or convert existing driver code that was built with previous versions of the WDK, the conversion process preserves the target version information (operating systems and platform). + +The default Solution build configuration is Windows 8.1 Debug and Win32. + +### To select a configuration and build a driver + +1. Open the driver project or solution in Visual Studio (find *filtername*.sln or *filtername*.vcxproj). +2. Right-click the solution in the **Solutions Explorer** and select **Configuration Manager**. +3. From the **Configuration Manager**, select the **Active Solution Configuration** (for example, Windows 8.1 Debug or Windows 8.1 Release) and the **Active Solution Platform** (for example, Win32) that correspond to the type of build you are interested in. +4. From the Build menu, click **Build Solution** (Ctrl+Shift+B). + +Building a Driver Using the Command Line (MSBuild) +-------------------------------------------------- + +You can build a driver from the command line using the Visual Studio Command Prompt window and the Microsoft Build Engine (MSBuild.exe) Previous versions of the WDK used the Windows Build utility (Build.exe) and provided separate build environment windows for each of the supported build configurations. You can now use the Visual Studio Command Prompt window for all build configurations. + +### To select a configuration and build a driver + +1. Open a Visual Studio Command Prompt window at the **Start** screen. From this window you can use MsBuild.exe to build any Visual Studio project by specifying the project (.VcxProj) or solutions (.Sln) file. +2. Navigate to the project directory and enter the **MSbuild** command for your target. For example, to perform a clean build of a Visual Studio driver project called *filtername*.vcxproj, navigate to the project directory and enter the following MSBuild command: **msbuild /t:clean /t:build .\\***filtername***.vcxproj**. + +Run the sample +-------------- + +Installation +------------ + +The minifilter samples come with an INF file that will install the minifilter. To install the minifilter, do the following: + +1. Make sure that *filtername*.sys and *filtername*.inf are in the same directory. + + **Note**  This installation will make the necessary registry updates to register the minifilter service and place *filtername*.sys in the %SystemRoot%\\system32\\drivers directory. + +2. In Windows Explorer, right-click *filtername*.inf, and click **Install**. + +3. To load the minifilter, run **fltmc load** *filtername* or **net start** *filtername*. + +Design and Operation +-------------------- + +The *NameChanger* minifilter illustrates how to make one part of a volume's namespace appear as though it belongs to part of another namespace. It accomplishes this by altering the names of files that reside beneath a particular path (called the "real mapping") to appear as though they actually reside beneath a different path (called the "user mapping"). The .inf file supplied with the sample defines the real and user mappings in the *[Strings]* section. The three strings used for the mappings are: + +<table> +<colgroup> +<col width="50%" /> +<col width="50%" /> +</colgroup> +<thead> +<tr class="header"> +<th align="left">String +Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td align="left"><p>UserMapping</p> +<p>The location where files will appear to be in when the filter is attached</p></td> +<td align="left"><p>UserMappingFinalComponentShort</p> +<p>The"short" (e.g. DOS-compliant 8.3-format) name for the final component of the UserMapping path.</p></td> +</tr> +</tbody> +</table> + +Before attaching the minifilter to a volume, you must set up the user and real paths. By default the .inf defines the mapping paths like in the following manner: + +<table> +<colgroup> +<col width="50%" /> +<col width="50%" /> +</colgroup> +<thead> +<tr class="header"> +<th align="left">String +Mapping</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td align="left"><p>UserMapping</p> +<p>"\X\Y"</p></td> +<td align="left"><p>UserMappingFinalComponentShort</p> +<p>"Y"</p></td> +</tr> +</tbody> +</table> + +To successfully attach the filter to a volume you must first create a couple of directories. For example, to attach the *NameChanger* minifilter to the F: volume, first create the RealMapping directory (the F:\\A\\B directory). Next, create the parent of the UserMapping path (the F:\\X directory). The following directories are be created: + +F:\\A\\B + +F:\\X + +Once this is done the *NameChanger* filter should successfully attach to F:. It will change the directories you created to appear like the following: + +F:\\A + +F:\\X\\Y + +After the minifilter attaches, the "B" subdirectory of F:\\A is no longer visible. Its contents now appear under the "Y" subdirectory of F:\\X. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/NameChanger/nc.c b/filesys/miniFilter/NameChanger/nc.c new file mode 100644 index 00000000..05431407 --- /dev/null +++ b/filesys/miniFilter/NameChanger/nc.c @@ -0,0 +1,2017 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + nc.c + +Abstract: + + This is the main module of the NameChanger MiniFilter driver. + This filter grafts a directory from one part of a volume's namespace + to another part using a mapping. The filter then maintains the illusion + by acting as a name provider, injecting entries into directory enumerations + and forwarding directory change notifications. + +Environment: + + Kernel mode + +--*/ + +#include <dontuse.h> +#include <suppress.h> + +#include "nc.h" + + +// +// Prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +NcInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +NcInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +NcUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +NcInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + + +// +// Assign sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, NcInstanceSetup) +#pragma alloc_text(PAGE, NcInstanceTeardownStart) +#pragma alloc_text(PAGE, NcUnload) +#pragma alloc_text(PAGE, NcInstanceQueryTeardown) +#endif + +// +// IRP Callback Prototypes +// + +FLT_PREOP_CALLBACK_STATUS +NcPreCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreCleanupCallback ( + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, + _Unreferenced_parameter_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreFsctlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostFsctlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode==FSCTL_FIND_FILES_BY_SID, _In_) + _When_(Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode!=FSCTL_FIND_FILES_BY_SID, _In_opt_) + PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreQueryInformationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostQueryInformationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreSetInformationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreDirectoryControlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostDirectoryControlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreNetworkQueryCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcPreCreateCallback) +#pragma alloc_text(PAGE, NcPreCleanupCallback) +#pragma alloc_text(PAGE, NcPostCleanupCallback) +#pragma alloc_text(PAGE, NcPreFsctlCallback) +#pragma alloc_text(PAGE, NcPreQueryInformationCallback) +#pragma alloc_text(PAGE, NcPostQueryInformationCallback) +#pragma alloc_text(PAGE, NcPreSetInformationCallback) +#pragma alloc_text(PAGE, NcPreDirectoryControlCallback) +#pragma alloc_text(PAGE, NcPreNetworkQueryCallback) +#endif + +// +// Name Callback Prototypes +// + +NTSTATUS +NcGenerateFileNameCallback ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL FileName + ); + +NTSTATUS +NcNormalizeNameComponentExCallback ( + _In_ PFLT_INSTANCE Instance, + _In_opt_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); + +NTSTATUS +NcNormalizeNameComponentCallback ( + _In_ PFLT_INSTANCE Instance, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcGenerateFileNameCallback) +#pragma alloc_text(PAGE, NcNormalizeNameComponentCallback) +#pragma alloc_text(PAGE, NcNormalizeNameComponentExCallback) +#endif + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + NcPreCreateCallback, + NULL }, + + { IRP_MJ_CLEANUP, + 0, + NcPreCleanupCallback, + NcPostCleanupCallback }, + + { IRP_MJ_QUERY_INFORMATION, + 0, + NcPreQueryInformationCallback, + NcPostQueryInformationCallback }, + + { IRP_MJ_SET_INFORMATION, + 0, + NcPreSetInformationCallback, + NULL }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + NcPreDirectoryControlCallback, + NcPostDirectoryControlCallback }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + 0, + NcPreNetworkQueryCallback, + NULL }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + NcPreFsctlCallback, + NcPostFsctlCallback }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context Registration +// + +const FLT_CONTEXT_REGISTRATION contextRegistration[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + NcInstanceContextClose, + sizeof(NC_INSTANCE_CONTEXT), + NC_INSTANCE_CONTEXT_TAG, + NULL, + NULL, + NULL }, + + { FLT_STREAMHANDLE_CONTEXT, + 0, + NcStreamHandleContextClose, + sizeof(NC_STREAM_HANDLE_CONTEXT), + NC_STREAM_HANDLE__CONTEXT_TAG, + NULL, + NULL, + NULL }, + + { FLT_CONTEXT_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + contextRegistration, // Context + Callbacks, // Operation callbacks + + NcUnload, // MiniFilterUnload + + NcInstanceSetup, // InstanceSetup + NcInstanceQueryTeardown, // InstanceQueryTeardown + NcInstanceTeardownStart, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NcGenerateFileNameCallback, // GenerateFileName + NcNormalizeNameComponentCallback, // Normalize Component + NULL, // NormalizeContextCleanupCallback + +#if FLT_MGR_LONGHORN + NULL, // Transaction Notification Callback. + NcNormalizeNameComponentExCallback // Normalize Name Component with transaction handle. +#endif + +}; + +// +// Constants +// + +NC_GLOBAL_DATA NcGlobalData; + +// +// Function Definitions +// + + +NTSTATUS +NcInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + + We attach to the volume if: + The User mapping parent exists. + The User mapping does not exist + The Real mapping parent exists + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + Other Errorcode - Percolate errors up. (do not attach). + +Logic: + A) Validate that we should attach. + 1) Construct UserMappingPath + 2) Open User Parent (validate it exists) + 3) Query Final component (validate it does not exist) + 4) Construct RealMappingPath + 5) Open Real Parent (validate it exists) + B) Now we know that we should attach. We need to get + short and normalized names for the paths and then + construct our mapping and instance context. +--*/ +{ + + // + // Status Tracking Vars. + // + + NTSTATUS Status; + NTSTATUS ReturnValue = STATUS_SUCCESS; + + // + // User mapping vars + // + + NC_MAPPING_PATH UserPath; + HANDLE UserParentHandle = 0; + PFILE_OBJECT UserParentFileObj = NULL; + OBJECT_ATTRIBUTES UserParentAttributes; + IO_STATUS_BLOCK UserParentStatusBlock; + PFILE_NAMES_INFORMATION FinalComponentQueryBuffer = NULL; + ULONG FinalComponentQueryLength; + + // + // Real Mapping vars + // + + NC_MAPPING_PATH RealPath; + HANDLE RealParentHandle = 0; + PFILE_OBJECT RealParentFileObj = NULL; + OBJECT_ATTRIBUTES RealParentAttributes; + IO_STATUS_BLOCK RealParentStatusBlock; + + // + // Context Vars + // + + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + + // + // Temp vars + // + + ULONG DataRead; + NC_PATH_OVERLAP Overlap; + + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( VolumeDeviceType ); + + // + // Although this filter goes to great lengths to be filesystem-agnostic, + // it is not yet able to handle the V3 USN records supported by ReFS. + // + + if (VolumeFilesystemType == FLT_FSTYPE_REFS) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Prevent the filter from auto attaching. + // + + if (FlagOn( Flags, FLTFL_INSTANCE_SETUP_AUTOMATIC_ATTACHMENT )) { + + // + // We actually return here, rather than goto cleanup, because + // we have not yet zeroed out the buffers in the mappings. + // + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // We should not be attaching before our driver initialization + // has completed, which should load and sanity check these + // values. If this hasn't happened, fail attachment. + // + + if (NcGlobalData.UserMappingPath.Length == 0 || + NcGlobalData.UserMappingFinalComponentShort.Length == 0 || + NcGlobalData.UserMappingFinalComponentLong.Length == 0 || + NcGlobalData.RealMappingPath.Length == 0 || + NcGlobalData.RealMappingFinalComponent.Length == 0) { + + FLT_ASSERT( FALSE ); + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Zero Out structures. (Now we can cleanup) + // + + NcInitMappingPath( &UserPath ); + NcInitMappingPath( &RealPath ); + + // + // Build the mapping strings for this instance from our global + // registry state. + // + + Status = NcBuildMappingPathFromVolume( FltObjects->Volume, + &NcGlobalData.UserMappingPath, + &NcGlobalData.UserMappingFinalComponentShort, + &UserPath ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + } + + Status = NcBuildMappingPathFromVolume( FltObjects->Volume, + &NcGlobalData.RealMappingPath, + &NcGlobalData.RealMappingFinalComponent, + &RealPath ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + } + + // + // Allocate the buffer to store the directory lookup. We + // reuse the buffer for long and short names, so it must + // be large enough to handle either. + // + + FinalComponentQueryLength = sizeof(FILE_NAMES_INFORMATION) + + AlignToSize( max( NcGlobalData.UserMappingFinalComponentLong.Length, NcGlobalData.UserMappingFinalComponentShort.Length ), 8 ); + FinalComponentQueryBuffer = ExAllocatePoolWithTag( PagedPool, + FinalComponentQueryLength, + NC_NORMALIZE_NAME_TAG ); + + if (FinalComponentQueryBuffer == NULL) { + + ReturnValue = STATUS_INSUFFICIENT_RESOURCES; + goto NcInstanceSetupCleanup; + } + + + // + // Open Parent of user mapping, and validate that it exists. + // Note that we do have a race here; a user may have this object + // opened with FILE_DELETE_ON_CLOSE. Under these semantics, our + // create will succeed, but when their handle is closed our + // mapping parent will go away, which this filter cannot deal + // with. + // + + InitializeObjectAttributes( &UserParentAttributes, + &UserPath.ParentPath, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + Status = NcCreateFileEx2( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &UserParentHandle, // Returned Handle + &UserParentFileObj, // Returned FileObject + FILE_LIST_DIRECTORY|FILE_TRAVERSE, // Desired Access + &UserParentAttributes, // object attributes + &UserParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + NULL ); // Transaction/ECP + + if (!NT_SUCCESS( Status )) { + + // + // We could not open the parent path, fail attachment. + // + + ReturnValue = STATUS_FLT_DO_NOT_ATTACH; + goto NcInstanceSetupCleanup; + } + + // + // Make sure that the User mapping's short final component does not exist. + // + + Status = NcQueryDirectoryFile( FltObjects->Instance, + UserParentFileObj, + FinalComponentQueryBuffer, + FinalComponentQueryLength, + FileNamesInformation, + TRUE,//Return single entry + &NcGlobalData.UserMappingFinalComponentShort, + TRUE,//restart scan + &DataRead); + + if (Status != STATUS_NO_SUCH_FILE) { + + if (!NT_SUCCESS( Status )) { + + // + // An unexpected error occurred, return code. + // + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + + } else { + + // + // The query worked, but the file exists. + // The volume does not meet our constraints, + // so abort attachment. + // + + ReturnValue = STATUS_FLT_DO_NOT_ATTACH; + goto NcInstanceSetupCleanup; + } + } + + // + // Now we have established that the short name does not exist, + // we must search again for the long name. Since the search + // string for a directory enumeration is cached by the filesystem + // and cannot be changed, we have to close and re-open the file + // in order to do the second enumeration. + // + + FltClose( UserParentHandle ); + UserParentHandle = NULL; + + ObDereferenceObject( UserParentFileObj ); + UserParentFileObj = NULL; + + Status = NcCreateFileEx2( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &UserParentHandle, // Returned Handle + &UserParentFileObj, // Returned FileObject + FILE_LIST_DIRECTORY|FILE_TRAVERSE, // Desired Access + &UserParentAttributes, // object attributes + &UserParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + NULL ); // Transaction/ECP + + if (!NT_SUCCESS( Status )) { + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + } + + // + // Make sure that the User mapping's long final component does not exist. + // + + Status = NcQueryDirectoryFile( FltObjects->Instance, + UserParentFileObj, + FinalComponentQueryBuffer, + FinalComponentQueryLength, + FileNamesInformation, + TRUE,//Return single entry + &NcGlobalData.UserMappingFinalComponentLong, + TRUE,//restart scan + &DataRead ); + + if (Status != STATUS_NO_SUCH_FILE) { + + if (!NT_SUCCESS( Status )) { + + // + // An unexpected error occurred, return code. + // + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + + } else { + + // + // The query worked, but the file exists. + // The volume does not meet our constraints, + // so abort attachment. + // + + ReturnValue = STATUS_FLT_DO_NOT_ATTACH; + goto NcInstanceSetupCleanup; + } + } + + // + // Open parent of real mapping. + // Note that we do have a race here; a user may have this object + // opened with FILE_DELETE_ON_CLOSE. Under these semantics, our + // create will succeed, but when their handle is closed our + // mapping parent will go away, which this filter cannot deal + // with. + // + + InitializeObjectAttributes( &RealParentAttributes, + &RealPath.ParentPath, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + Status = NcCreateFileEx2( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &RealParentHandle, // Returned Handle + &RealParentFileObj, // Returned FileObject + FILE_LIST_DIRECTORY|FILE_TRAVERSE, // Desired Access + &RealParentAttributes, // object attributes + &RealParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + NULL ); // Transaction/ECP + + FLT_ASSERT( Status != STATUS_INVALID_PARAMETER ); + + if (!NT_SUCCESS( Status )) { + + // + // We could not open the parent path, fail attachment. + // + + ReturnValue = STATUS_FLT_DO_NOT_ATTACH; + goto NcInstanceSetupCleanup; + } + + // + // We have validated that we should attach. + // + + + // + // Allocate context + // + + Status = FltAllocateContext( FltObjects->Filter, + FLT_INSTANCE_CONTEXT, + sizeof(NC_INSTANCE_CONTEXT), + NonPagedPool, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + } + + // + // We have allocated an instance context. Now initialize it. + // + + // + // Now that we have a context, + // we need to generate the mapping + // + + NcInitMapping( &InstanceContext->Mapping ); + + + Status = NcBuildMapping( UserParentFileObj, + RealParentFileObj, + &NcGlobalData.UserMappingFinalComponentShort, + &NcGlobalData.UserMappingFinalComponentLong, + &NcGlobalData.RealMappingFinalComponent, + FltObjects->Instance, + &InstanceContext->Mapping ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + } + + // + // Sanity check the relation between the mappings. Force case insensitive + // to TRUE here, since we may be evaluating this relationship in response + // to case insensitive user requests in the future. + // + + NcComparePath( &RealPath.FullPath, + &InstanceContext->Mapping.UserMapping, + NULL, + TRUE, + TRUE, + &Overlap ); + + if (Overlap.InMapping || Overlap.Ancestor) { + + ReturnValue = STATUS_FLT_DO_NOT_ATTACH; + goto NcInstanceSetupCleanup; + } + + NcComparePath( &UserPath.FullPath, + &InstanceContext->Mapping.RealMapping, + NULL, + TRUE, + TRUE, + &Overlap ); + + if (Overlap.InMapping || Overlap.Ancestor) { + + ReturnValue = STATUS_FLT_DO_NOT_ATTACH; + goto NcInstanceSetupCleanup; + } + + // + // Remember what file system we're attached to. + // + + InstanceContext->VolumeFilesystemType = VolumeFilesystemType; + + // + // Register the instance context. + // + + Status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_REPLACE_IF_EXISTS, + InstanceContext, + NULL); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = Status; + goto NcInstanceSetupCleanup; + } + + +NcInstanceSetupCleanup: + + if (InstanceContext != NULL ) { + + FltReleaseContext( InstanceContext ); + } + + NcTeardownMappingPath( &UserPath ); + + if (UserParentHandle != 0) { + + FltClose( UserParentHandle ); + } + + if (UserParentFileObj != NULL) { + + ObDereferenceObject( UserParentFileObj ); + } + + NcTeardownMappingPath( &RealPath ); + + if (RealParentHandle != 0) { + + FltClose( RealParentHandle ); + } + + if (RealParentFileObj != NULL) { + + ObDereferenceObject( RealParentFileObj ); + } + + if (FinalComponentQueryBuffer != NULL) { + + ExFreePool( FinalComponentQueryBuffer ); + } + + return ReturnValue; +} + + +NTSTATUS +NcInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the Status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + return STATUS_SUCCESS; +} + + +VOID +NcInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + NTSTATUS Status; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( Flags ); + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcInstanceTeardownStartCleanup; + } + +NcInstanceTeardownStartCleanup: + + if (InstanceContext) { + + FltReleaseContext( InstanceContext ); + } + +} + +// +// MiniFilter initialization and unload routines. +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS Status; + + // + // Import routines + // + + NcCompatInit( ); + + Status = NcInitializeMapping( RegistryPath ); + if (!NT_SUCCESS( Status )) { + return Status; + } + + // + // Register with FltMgr to tell it our callback routines + // + + Status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &NcGlobalData.FilterHandle ); + + FLT_ASSERT( NT_SUCCESS( Status ) ); + + if (NT_SUCCESS( Status )) { + + // + // Start filtering i/o + // + + Status = FltStartFiltering( NcGlobalData.FilterHandle ); + + if (!NT_SUCCESS( Status )) { + + FltUnregisterFilter( NcGlobalData.FilterHandle ); + } + } + + return Status; +} + +NTSTATUS +NcUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final Status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + FltUnregisterFilter( NcGlobalData.FilterHandle ); + + if (NcGlobalData.UserMappingPath.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.UserMappingPath ); + } + + if (NcGlobalData.UserMappingFinalComponentShort.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.UserMappingFinalComponentShort ); + } + + if (NcGlobalData.UserMappingFinalComponentLong.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.UserMappingFinalComponentLong ); + } + + if (NcGlobalData.RealMappingPath.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.RealMappingPath ); + } + + if (NcGlobalData.RealMappingFinalComponent.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.RealMappingFinalComponent ); + } + + return STATUS_SUCCESS; +} + +// +// MiniFilter callback routines. +// + +FLT_PREOP_CALLBACK_STATUS +NcPreCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Routine is invoked when a create is issued by a user. + + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +Logic: + + 1) If file is opened by ID or is a Paging File, etc. + A) return PASSTHROUGH_NO_CALLBACK + 2) Get the opened name and calculate overlap + 3) If File is Delete On close + A) If the file is an ancestor of either mapping + a) Fail Open + 4) If the path is in the real mapping + A) Fail the Open + 5) If the path is in user mapping + A) Recalculate the full name (without SL_TARGET_PARENT_DIRECTORY) + B) Munge Name to Real Mapping + C) Switch name + D) Passthrough with callback. + 6) Else + A) Return Passthrough No callback +--*/ +{ + PAGED_CODE(); + + return NcPreCreate( Data, + FltObjects, + CompletionContext ); +} + +FLT_PREOP_CALLBACK_STATUS +NcPreCleanupCallback ( + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, + _Unreferenced_parameter_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This is the pre cleanup callback. It is called for every handle cleanup + operation. The role of this callback is merely to ensure that we + also receive every post handle cleanup operation, and that it is safe + to issue filesystem calls when we do. We do this via + FLT_PREOP_SYNCHRONIZE for simplicity. + parameter. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. Not used in this function. + +Return Value: + + Returns the final status of this operation. Always FLT_PREOP_SYNCHRONIZE + in this routine. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + // + // Synchronize all cleanup requests. This enables us to ensure + // that any state is torn down correctly even if a request is + // issued between pre cleanup and post cleanup. + // + + return FLT_PREOP_SYNCHRONIZE; +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the post cleanup callback. It is called for every handle cleanup + operation. The role of this callback is to tear down any pending + directory change notification state attached to this handle. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. Not used in this function. + + Flags - The flags for this operation. + +Return Value: + + Returns the final status of this operation. Always + FLT_POSTOP_FINISHED_PROCESSING in this routine. Note that cleanup + operations should never fail. + +--*/ +{ + NTSTATUS Status; + PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) { + + // + // Obtain our handle context. We should only be called here + // if we really have one. + // + + Status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &HandleContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostCleanupCallbackCleanup; + + } + + FLT_ASSERT( HandleContext != NULL ); + + NcStreamHandleContextNotCleanup( HandleContext ); + } + + +NcPostCleanupCallbackCleanup: + + if (HandleContext != NULL) { + + FltReleaseContext( HandleContext ); + + } + + // + // We don't want to fail cleanup no matter what goes wrong. + // + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +NcPreFsctlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This is the pre FSCTL callback. It is called for every FSCTL + operation. This allows us to intercept and process FSCTLs which + either consume or return names which may be affected by the namespace + redirection performed by this filter. + + Currently we munge: + FSCTL_ENUM_USN_DATA + FSCTL_FIND_FILES_BY_SID + FSCTL_LOOKUP_STREAM_FROM_CLUSTER + FSCTL_READ_FILE_USN_DATA + FSCTL_READ_USN_JOURNAL + + We also need to munge: + FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. Not used in this function. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + PAGED_CODE(); + + // + // TODO: + // FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES + // + // The following take paths and work without any explicit change. + // Since Txf sends its creates to the top of the filter stack, we + // munge these paths on create (both for the RM and the logs.) Thus + // Txf believes its logs are in the user mapping, when they are not; + // subsequent queries return the user mapping correctly. + // + // FSCTL_TXFS_QUERY_RM_INFORMATION + // FSCTL_TXFS_START_RM + // + + switch (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode) { + case FSCTL_ENUM_USN_DATA: + return FLT_PREOP_SYNCHRONIZE; + break; + case FSCTL_FIND_FILES_BY_SID: + return NcPreFindFilesBySid( Data, + FltObjects, + CompletionContext ); + break; +#if FLT_MGR_WIN7 + case FSCTL_LOOKUP_STREAM_FROM_CLUSTER: + return FLT_PREOP_SYNCHRONIZE; + break; +#endif + case FSCTL_READ_FILE_USN_DATA: + return FLT_PREOP_SYNCHRONIZE; + break; + case FSCTL_READ_USN_JOURNAL: + return FLT_PREOP_SUCCESS_WITH_CALLBACK; + break; + default: + + // + // We're not processing this. Let the filesystem handle it + // normally. + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + break; + + } +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostFsctlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode==FSCTL_FIND_FILES_BY_SID, _In_) + _When_(Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode!=FSCTL_FIND_FILES_BY_SID, _In_opt_) + PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the post FSCTL callback. It is called for every FSCTL + which we need to munge, including USN, find files by SID, and lookup + stream from cluster. This allows us to process these FSCTLs which + either consume or return names which may be affected by the namespace + redirection performed by this filter. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. Not used in this function. + + Flags - The flags for this operation. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + FLT_POSTOP_CALLBACK_STATUS Status = FLT_POSTOP_FINISHED_PROCESSING; + + if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) { + + switch (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode) { + + case FSCTL_ENUM_USN_DATA: + + Status = NcPostEnumUsnData( Data, + FltObjects, + CompletionContext, + Flags ); + break; + + case FSCTL_FIND_FILES_BY_SID: + + Status = NcPostFindFilesBySid( Data, + FltObjects, + CompletionContext, + Flags ); + + break; +#if FLT_MGR_WIN7 + case FSCTL_LOOKUP_STREAM_FROM_CLUSTER: + + Status = NcPostLookupStreamFromCluster( Data, + FltObjects, + CompletionContext, + Flags ); + + break; +#endif + + case FSCTL_READ_FILE_USN_DATA: + + Status = NcPostReadFileUsnData( Data, + FltObjects, + CompletionContext, + Flags ); + break; + + case FSCTL_READ_USN_JOURNAL: + + Status = NcPostReadUsnJournal( Data, + FltObjects, + CompletionContext, + Flags ); + break; + + default: + break; + } + } + + return Status; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreQueryInformationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This callback is invoked when the user queries a file's information. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + Currently we munge: + FileAllInformation + FileAlternateNameInformation + FileHardLinkInformation + FileNameInformation + FileNormalizedNameInformation + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS result; + + PAGED_CODE(); + + // + // We only have to munge for name sensitive operations. + // + + switch( Data->Iopb->Parameters.QueryFileInformation.FileInformationClass ) { + + case FileAllInformation: + case FileNameInformation: + case FileNormalizedNameInformation: + + result = FLT_PREOP_SYNCHRONIZE; + break; + + case FileAlternateNameInformation: + result = NcPreQueryAlternateName( Data, + FltObjects, + CompletionContext ); + break; + + case FileHardLinkInformation: + + result = FLT_PREOP_SYNCHRONIZE; + break; + + default: + result = FLT_PREOP_SUCCESS_NO_CALLBACK; + break; + } + + return result; +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostQueryInformationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the post query file information callback. It is called for + every information class we need to process. This allows us to fixup + names returned that would otherwise contain real mapping paths. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. Not used in this function. + + Flags - The flags for this operation. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + FLT_POSTOP_CALLBACK_STATUS Status = FLT_POSTOP_FINISHED_PROCESSING; + + PAGED_CODE(); + + if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) { + + switch( Data->Iopb->Parameters.SetFileInformation.FileInformationClass ) { + + case FileNameInformation: + case FileNormalizedNameInformation: + case FileAllInformation: + + Status = NcPostQueryName( Data, + FltObjects, + CompletionContext, + Flags ); + break; + + case FileHardLinkInformation: + Status = NcPostQueryHardLinks( Data, + FltObjects, + CompletionContext, + Flags ); + break; + } + } + return Status; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreSetInformationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This is the pre set file information callback. It is called for every + set file information operation. This allows us to intercept and process + those which either consume or return names, as well as those which would + violate the constraints imposed by this filter. + + Currently we munge: + FileDispositionInformation + FileLinkInformation + FileRenameInformation + FileShortNameInformation + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + + FLT_PREOP_CALLBACK_STATUS result; + + PAGED_CODE(); + + switch( Data->Iopb->Parameters.SetFileInformation.FileInformationClass ) { + + case FileDispositionInformation: + + result = NcPreSetDisposition( Data, + FltObjects, + CompletionContext ); + + break; + + case FileLinkInformation: + + result = NcPreSetLinkInformation( Data, + FltObjects, + CompletionContext ); + + break; + + case FileRenameInformation: + + result = NcPreRename( Data, + FltObjects, + CompletionContext ); + + break; + + case FileShortNameInformation: + + result = NcPreSetShortName( Data, + FltObjects, + CompletionContext ); + + break; + + default: + result = FLT_PREOP_SUCCESS_NO_CALLBACK; + break; + } + + return result; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreDirectoryControlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This callback is invoked when the user tries to enumerate a directory + or get change notifications. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS Status; + + PAGED_CODE(); + + switch( Data->Iopb->MinorFunction ) { + + case IRP_MN_QUERY_DIRECTORY: + Status = NcEnumerateDirectory( Data, FltObjects, CompletionContext ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + Status = NcPreNotifyDirectory( Data, FltObjects, CompletionContext ); + break; + + default: + FLT_ASSERT( FALSE ); + Status = FLT_PREOP_SUCCESS_NO_CALLBACK; + + } + + return Status; +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostDirectoryControlCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This callback is invoked after the user tries to enumerate a directory + or get change notifications. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + FLT_POSTOP_CALLBACK_STATUS Status; + + // + // Note this routine must be nonpaged. It is called within completion + // routines. + // + + switch( Data->Iopb->MinorFunction ) { + + case IRP_MN_QUERY_DIRECTORY: + + // + // Currently we don't process post directory enumeration + // requests. We really should. + // + + Status = FLT_POSTOP_FINISHED_PROCESSING; + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + + Status = NcPostNotifyDirectory( Data, FltObjects, CompletionContext, Flags ); + break; + + default: + FLT_ASSERT( FALSE ); + Status = FLT_POSTOP_FINISHED_PROCESSING; + + } + + return Status; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreNetworkQueryCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This callback is invoked when the user tries to do a network query open. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + // + // TODO We need to pipe these through the existing create path if + // possible. + // + + return FLT_PREOP_DISALLOW_FASTIO; +} + +NTSTATUS +NcGenerateFileNameCallback ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL OutputNameControl + ) +/*++ + +Routine Description: + + This callback is invoked to maintain FltMgr's name cache coherency + when this filter munges names. + +Arguments: + + Instance - Instance pointer for the minifilter instance that this + callback routine is registered for. + + FileObject - Pointer to a file object for the file whose name is being + requested. + + Data - Pointer to the callback data structure for the operation during + which this name is being requested. + + NameOptions - FLT_FILE_NAME_OPTIONS value that specifies the name format, + query method, and flags for this file name information query. + + CacheFileNameInformation - Pointer to a Boolean value specifying whether + this name can be cached. Set to TRUE on output if the name can be + cached; set to FALSE otherwise. + + OutputNameControl - Pointer to a Filter Manager-allocated FLT_NAME_CONTROL + structure to receive the file name on output. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + + PAGED_CODE(); + + return NcGenerateFileName( Instance, + FileObject, + Data, + NameOptions, + CacheFileNameInformation, + OutputNameControl ); +} + +NTSTATUS +NcNormalizeNameComponentExCallback ( + _In_ PFLT_INSTANCE Instance, + _In_opt_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +/*++ + +Routine Description: + + This callback is invoked to maintain FltMgr's name cache coherency + when this filter munges names. + +Arguments: + + Instance - Instance pointer for the minifilter instance that this callback + routine is registered for. + + FileObject - Optionally points to a file object that the name query is + taking place on. We can use this to flow transaction information + to any recursive requests. + + ParentDirectory - Pointer to a UNICODE_STRING structure that contains the + name of the parent directory for this name component. + + DeviceNameLength - Length, in bytes, of the parent directory name stored + in the structure that the ParentDirectory parameter points to. + + Component - Pointer to a UNICODE_STRING structure that contains the name + component to be expanded. + + ExpandComponentName - Pointer to a FILE_NAMES_INFORMATION structure that + receives the expanded (normalized) file name information for the name + component. + + ExpandComponentNameLength - Length, in bytes, of the buffer that the + ExpandComponentName parameter points to. + + NormalizationContext - Pointer to minifilter-provided context information + to be passed in any subsequent calls to this callback routine that are + made to normalize the remaining components in the same file name path. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PAGED_CODE(); + + return NcNormalizeNameComponentEx( Instance, + FileObject, + ParentDirectory, + DeviceNameLength, + Component, + ExpandComponentName, + ExpandComponentNameLength, + Flags, + NormalizationContext ); + +} + +NTSTATUS +NcNormalizeNameComponentCallback ( + _In_ PFLT_INSTANCE Instance, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +/*++ + +Routine Description: + + This callback is invoked to maintain FltMgr's name cache coherency + when this filter munges names. + +Arguments: + + Instance - Instance pointer for the minifilter instance that this callback + routine is registered for. + + ParentDirectory - Pointer to a UNICODE_STRING structure that contains the + name of the parent directory for this name component. + + DeviceNameLength - Length, in bytes, of the parent directory name stored + in the structure that the ParentDirectory parameter points to. + + Component - Pointer to a UNICODE_STRING structure that contains the name + component to be expanded. + + ExpandComponentName - Pointer to a FILE_NAMES_INFORMATION structure that + receives the expanded (normalized) file name information for the name + component. + + ExpandComponentNameLength - Length, in bytes, of the buffer that the + ExpandComponentName parameter points to. + + NormalizationContext - Pointer to minifilter-provided context information + to be passed in any subsequent calls to this callback routine that are + made to normalize the remaining components in the same file name path. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + + PAGED_CODE(); + + return NcNormalizeNameComponentEx( Instance, + NULL, + ParentDirectory, + DeviceNameLength, + Component, + ExpandComponentName, + ExpandComponentNameLength, + Flags, + NormalizationContext ); +} + + + diff --git a/filesys/miniFilter/NameChanger/nc.h b/filesys/miniFilter/NameChanger/nc.h new file mode 100644 index 00000000..c2509faa --- /dev/null +++ b/filesys/miniFilter/NameChanger/nc.h @@ -0,0 +1,1040 @@ +#ifndef __NC_H__ +#define __NC_H__ + +#pragma warning( disable: 4701 ) // 'Potentially' uninitialized local variable. + // Hopefully one day the compiler will be a little + // more factual about this and we can use it. It + // would make a great warning. + +// +// Allocation Tags +// + +#define NC_TAG 'NC__' // Generic NameChanger Tag +#define NC_MAPPING_TAG 'NCMP' // Mapping Buffer Tag +#define NC_INSTANCE_CONTEXT_TAG 'NCin' // Instance Context Tag +#define NC_LOCK_TAG 'NClk' // Tag for nonpaged locks +#define NC_GENERATE_NAME_TAG 'NCgn' // Tag for strings which are allocated for name generation +#define NC_NORMALIZE_NAME_TAG 'NCnn' // Tag for strings which are allocated for name normalization +#define NC_FILE_NAME_TAG 'NCfn' // Tag for strings which are allocated for file names in file objects +#define NC_DIR_QRY_CACHE_TAG 'NCqc' // Tag for buffers which are allocated for directory enumeration cache and injection entries +#define NC_DIR_QRY_SEARCH_STRING 'NCqs' // Tag for strings which are allocated for directory search strings +#define NC_SET_LINK_BUFFER_TAG 'NCsl' // Tag for munge buffer in Set Link operations +#define NC_RENAME_BUFFER_TAG 'NCrn' // Tag for munge buffer in Rename operations + + +#define NC_STREAM_HANDLE__CONTEXT_TAG 'NCsh' + +// +// Macros to make math easier. +// + +#define NC_SEPARATOR ((WCHAR) L'\\') +#define EMPTY_UNICODE_STRING {0, 0, NULL} + +#define AlignToSize(_length, _alignment) \ + (((_length) + ((_alignment) - 1)) & ~((_alignment) - 1)) + +#define Max(a,b) ((a) >= (b) ? (a) : (b)) + +#define NcFreeUnicodeString( UCS ) \ + ExFreePoolWithTag( (UCS)->Buffer, NC_TAG ); \ + (UCS)->Buffer = NULL; \ + (UCS)->MaximumLength = \ + (UCS)->Length = 0; + +// +// We need to supress a few warnings in order to enable compilation with +// the 2003 IFS Kit. WDK does not require this. +// + +#pragma warning( push ) +#pragma warning( disable: 4201 4214 ) +#include <fltkernel.h> +#pragma warning( pop ) + +#include <ntintsafe.h> + +#define INVALID_HANDLE_VALUE ((HANDLE) -1) + +// +// Typedefs for dynamically imported routines +// + +typedef +NTSTATUS +(* NC_REPLACE_FILEOBJECT_NAME_TYPE) ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ); + +typedef +NTSTATUS +( * NC_QUERY_DIRECTORY_FILE_TYPE) ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(Length) PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ); + +typedef +NTSTATUS +( * NC_CREATE_FILE_EX_TYPE) ( + _In_ PFLT_FILTER Filter, + _In_opt_ PFLT_INSTANCE Instance, + _Out_ PHANDLE FileHandle, + _Outptr_opt_ PFILE_OBJECT *FileObject, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, + _In_ ULONG EaLength, + _In_ ULONG Flags + ); + +typedef +NTSTATUS +( * NC_CREATE_FILE_EX2_TYPE) ( + _In_ PFLT_FILTER Filter, + _In_opt_ PFLT_INSTANCE Instance, + _Out_ PHANDLE FileHandle, + _Outptr_opt_ PFILE_OBJECT *FileObject, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, + _In_ ULONG EaLength, + _In_ ULONG Flags, + _In_opt_ PIO_DRIVER_CREATE_CONTEXT DriverContext + ); + +typedef +PVOID +( * NC_GET_NEW_SYSTEM_BUFFER_ADDRESS ) ( + _In_ PFLT_CALLBACK_DATA Data + ); + +// +// Common Structures +// + +// +// Struct which provides a single buffer and offset for data caching. +// + +typedef struct __NC_CACHE_ENTRY { + + char *Buffer; + ULONG CurrentOffset; + +} NC_CACHE_ENTRY, *PNC_CACHE_ENTRY; + +// +// Structure which manages a single path. These are used to precompute +// various paths for the mapping for easy comparison. We do not use +// this structure for user-supplied paths (eg. on open.) +// + +typedef struct _NC_MAPPING_PATH { + + UNICODE_STRING FullPath; // \volume_name\parent_path\final_component + UNICODE_STRING VolumePath; // \volume_name + UNICODE_STRING ParentPath; // \volume_name\parent_name + UNICODE_STRING FinalComponentName; // final_component or \ (for volume root open) + UNICODE_STRING VolumelessName; // \parent_name\final_component + USHORT NumberComponentsInFullPath; // \volume_name\parent_name\final_component == 3 + USHORT NumberComponentsInVolumePath; // \volume_name == 1 + +} NC_MAPPING_PATH, *PNC_MAPPING_PATH; + +// +// Structure which manages a long and short name pairing. +// + +typedef struct _NC_MAPPING_ENTRY { + + //Strings which manage the long name. + NC_MAPPING_PATH LongNamePath; + + //Strings which manage the short name. + NC_MAPPING_PATH ShortNamePath; + +} NC_MAPPING_ENTRY, *PNC_MAPPING_ENTRY; + +// +// Structure which manages with user and real mapping. +// + +typedef struct _NC_MAPPING { + + NC_MAPPING_ENTRY RealMapping; + NC_MAPPING_ENTRY UserMapping; + +} NC_MAPPING, *PNC_MAPPING; + +// +// NC_PATH_OVERLAP is a series of flags which determine a name's relation to a mapping. +// +// Ancestor means that the path is a directory along the mapping path. +// For example \a\b is an ancestor of \a\b\c, but \a\b\x is not an ancestor of \a\b\c. +// +// Parent means that the path is the parent directory of the mapping path. +// +// Match means that the path and the mapping are equal. +// +// InMapping means that the path is either the mapping itself, or a decendent of the mapping. +// +// Peer means that the object is directly next to the mapping (a\b\x is a peer of a\b\y.) +// +// If no flags are set, the path is entirely outside the mapping. +// + +// +// TODO: Should we use a different representation of this structure which +// does not require warning suppressions? +// +#pragma warning( push ) +#pragma warning( disable: 4201 ) +typedef union { + ULONG EntireFlags; + struct { + int Ancestor:1; + int Parent:1; + int Match:1; + int InMapping:1; + int Peer:1; + }; +} NC_PATH_OVERLAP, *PNC_PATH_OVERLAP; +#pragma warning( pop ) + +// +// Instance Context Defines +// + +typedef struct _NC_INSTANCE_CONTEXT { + + // The mapping is defined as a property of the instance. + NC_MAPPING Mapping; + + // The file system we're attached to + FLT_FILESYSTEM_TYPE VolumeFilesystemType; + +} NC_INSTANCE_CONTEXT, *PNC_INSTANCE_CONTEXT; + + +// +// This context is used when a file is enumerating +// a directory and we need to cache entries in our context. +// + +typedef struct _NC_DIR_QRY_CONTEXT { + + BOOLEAN EnumerationOutstanding; + + // Determines if the structure is in use. If it is, + // then there is no need to initialize the cache or injection entry. + BOOLEAN InUse; + + // Pointer to list of entries which we must drain from. + NC_CACHE_ENTRY Cache; + + // Pointer to entry which we want to inject. + NC_CACHE_ENTRY InjectionEntry; + + // The user provided search string which can only be set up on + // the first query. + UNICODE_STRING SearchString; + + // The information class which the user requested. + FILE_INFORMATION_CLASS InformationClass; + +} NC_DIR_QRY_CONTEXT, *PNC_DIR_QRY_CONTEXT; + +// +// This context is used when a file is being used for +// directory notification forwarding. +// + +typedef struct _NC_DIR_NOT_IO_CACHE { + + // + // Stores the state of the cache. + // If the pointer is NULL, there is nothing in cache. + // + + NC_CACHE_ENTRY Cache; + + // + // If ReadyToIssue is TRUE, then its safe to fire off the request. + // + + BOOLEAN ReadyToIssue; + + // + // Io is a pointer to the callback data. + // When ReadyToUse new io can be issued. + // + + PFLT_CALLBACK_DATA Io; + + // + // SystemBuffer is a pointer to a system buffer which will + // be used to issue the IO. + // + + char * SystemBuffer; + ULONG SystemBufferLength; + +} NC_DIR_NOT_IO_CACHE, * PNC_DIR_NOT_IO_CACHE; + +typedef struct _NC_DIR_NOT_CONTEXT { + + // + // We save off some parameters used to send down the user + // request so we can look them up in our completion callback. + // In some cases, this is an optimization. In others, it's + // because we need access to state when the user request may + // be completed. + // + + enum { + Uninitialized, + Filter, + Munge, + Merge + } Mode; + BOOLEAN CancelSeen; + BOOLEAN CleanupSeen; + BOOLEAN IgnoreCase; + BOOLEAN InsufficientBufferSeen; + BOOLEAN WatchTree; + UCHAR OperationFlags; + ULONG CompletionFilter; + + // + // These names allow us to re-build full path names for each + // notification. We need two strings to represent paths to + // the handle the user opened, or the handle we opened to + // the mapping parent when merging notifications. After + // building the full name, we'll need to re-translate back + // to the user's path for returning. These names are saved + // so we don't need to construct them again in our completion + // routine. + // + + UNICODE_STRING UserRequestName; + UNICODE_STRING MappingParentName; + + // + // Our three potential requests. + // + // UserRequest - the request the user sent to us on this handle. + // We may have sent this to the filesystem (Munge case), or we + // may hang on to it ourselves (Filter and Merge cases.) + // + // ShadowRequest - the request sent to the filesystem on the + // user's handle. This is used in the Filter and Merge cases. + // In the Filter case, we do this to allow us to reissue + // the IO if we have filtered out all events so have nothing + // to return to the user. In the merge case, we require two + // requests that we control, and will complete the user's + // request when either of our requests complete. + // + // MappingRequest - the request sent to the filesystem on the + // handle we opened to the parent of the mapping. Only valid + // when in Merge mode. We use the parent so as to capture + // notifications about changes to the mapping itself. + // + + PFLT_CALLBACK_DATA UserRequest; + PFLT_CALLBACK_DATA ShadowRequest; + PFLT_CALLBACK_DATA MappingRequest; + + PNC_INSTANCE_CONTEXT InstanceContext; + + // + // Specifies a handle to the parent of the real mapping. + // This can be NULL, and is only used if Mode == Merge. + // This occurs when we are watching the parent of the + // mapping, or if we are watching the subtree of the user + // mapping but not the subtree of the real mapping. Keep + // a workitem so that we can reliably clean up this object, + // which we can't do inline at APC level. + // + + HANDLE RealParentHandle; + PFILE_OBJECT RealParentFileObject; + PFLT_GENERIC_WORKITEM RealParentCloseWorkItem; + + PFILE_NOTIFY_INFORMATION BufferToFree; + ULONG BufferLength; + +} NC_DIR_NOT_CONTEXT, *PNC_DIR_NOT_CONTEXT; + +typedef struct _NC_FIND_BY_SID_CONTEXT { + + // + // Specifies a handle to the real mapping. This can + // be NULL, and is only required if finding objects + // under an ancestor of the user mapping which is not + // an ancestor of the real mapping (so we need to + // inject entries.) + // + + HANDLE RealHandle; + PFILE_OBJECT RealFileObject; + + // + // If the filesystem returns more results than can + // be returned in the current call, we need to buffer + // them and return them on a subsequent call. + // BufferToFree refers to the beginning of the allocation, + // which we can tear down when all entries are consumed. + // BufferSize is the size of this buffer, in bytes. + // CurrentEntry is the offset into this buffer where + // we should begin returning result on the subsequent + // call. + // + + PFILE_NAME_INFORMATION BufferToFree; + ULONG BufferSize; + ULONG CurrentEntry; + + // + // Count of the number of outstanding requests. We need + // to know this to ensure we only tear down state when + // a) holding the lock and b) no other requests are + // outstanding. + // + + ULONG OutstandingRequests; + +} NC_FIND_BY_SID_CONTEXT, *PNC_FIND_BY_SID_CONTEXT; + +// +// Stream Handle Context +// + +typedef struct _NC_STREAM_HANDLE_CONTEXT { + + PERESOURCE Lock; + + NC_DIR_NOT_CONTEXT DirectoryNotificationContext; + NC_DIR_QRY_CONTEXT DirectoryQueryContext; + NC_FIND_BY_SID_CONTEXT FindBySidContext; + +} NC_STREAM_HANDLE_CONTEXT, *PNC_STREAM_HANDLE_CONTEXT; + +#define NcLockStreamHandleContext( C ) \ + FltAcquireResourceExclusive( (C)->Lock ); + +#define NcUnlockStreamHandleContext( C ) \ + FltReleaseResource( (C)->Lock ); + +// +// Structure which gives us offsets to find values within directory +// query buffers. +// + +typedef struct _DIRECTORY_CONTROL_OFFSETS { + + ULONG NextEntryOffsetDist; + ULONG FileNameLengthDist; + ULONG FileNameDist; + + BOOLEAN ShortNamePresent; + ULONG ShortNameLengthDist; + ULONG ShortNameDist; + +} DIRECTORY_CONTROL_OFFSETS, *PDIRECTORY_CONTROL_OFFSETS; + +typedef struct _NC_GLOBAL_DATA { + UNICODE_STRING UserMappingPath; + UNICODE_STRING UserMappingFinalComponentShort; + UNICODE_STRING UserMappingFinalComponentLong; + + UNICODE_STRING RealMappingPath; + UNICODE_STRING RealMappingFinalComponent; + + PFLT_FILTER FilterHandle; +} NC_GLOBAL_DATA, *PNC_GLOBAL_DATA; + +extern NC_GLOBAL_DATA NcGlobalData; + +// +// Function declarations +// + +// +// The following functions exist in nchelper.c +// + +_When_(Data == NULL, _Pre_satisfies_(FileObject != NULL && Instance != NULL)) +_When_(FileObject == NULL || Instance == NULL, _Pre_satisfies_(Data != NULL)) +NTSTATUS +NcGetFileNameInformation( + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_opt_ PFILE_OBJECT FileObject, + _In_opt_ PFLT_INSTANCE Instance, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Outptr_ PFLT_FILE_NAME_INFORMATION *FileNameInformation + ); + +NTSTATUS +NcAllocateEResource( + _Out_ PERESOURCE * OutputLock + ); + +VOID +NcFreeEResource( + _In_ PERESOURCE Lock + ); + +NTSTATUS +NcCreateFileHelper ( + _In_ PFLT_FILTER Filter, + _In_opt_ PFLT_INSTANCE Instance, + _Out_ PHANDLE FileHandle, + _Outptr_opt_ PFILE_OBJECT *FileObject, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, + _In_ ULONG EaLength, + _In_ ULONG Flags, + _In_opt_ PFILE_OBJECT ParentFileObject + ); + +NTSTATUS +NcSetCancelCompletion ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PFLT_COMPLETE_CANCELED_CALLBACK CanceledCallback + ); + +LONG +NcExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ); + +// +// The following functions exist in ncmapping.c +// + +VOID +NcInitMappingPath ( + _Out_ PNC_MAPPING_PATH Path + ); + +VOID +NcTeardownMappingPath ( + _Inout_ PNC_MAPPING_PATH Path + ); + +NTSTATUS +NcBuildMappingPathFromVolume ( + _In_ PFLT_VOLUME CONST Volume, + _In_ PUNICODE_STRING ParentPath, + _In_ PUNICODE_STRING FinalComponentName, + _Inout_ PNC_MAPPING_PATH Entry + ); + +BOOLEAN +NcIsMappingZeroed ( + PNC_MAPPING Mapping + ); + +VOID +NcInitMapping ( + PNC_MAPPING Mapping + ); + +BOOLEAN +NcIsMappingZeroed ( + PNC_MAPPING Mapping + ); + +VOID +NcInitMapping ( + PNC_MAPPING Mapping + ); + +VOID +NcTeardownMapping ( + _Inout_ PNC_MAPPING Mapping + ); + +NTSTATUS +NcBuildMapping ( + _In_ PFILE_OBJECT UserParent, + _In_ PFILE_OBJECT RealParent, + _In_ PUNICODE_STRING UserFinalComponentShortName, + _In_ PUNICODE_STRING UserFinalComponentLongName, + _In_ PUNICODE_STRING RealFinalComponentName, + _In_ PFLT_INSTANCE Instance, + _Out_ PNC_MAPPING Mapping + ); + +// +// The following functions exist in ncinit.c +// + +NTSTATUS +NcInitializeMapping( + _In_ PUNICODE_STRING RegistryPath + ); + +// +// The following functions exist in ncpath.c +// + +BOOLEAN +NcComparePath ( + _In_ PCUNICODE_STRING Name, + _In_ PNC_MAPPING_ENTRY Mapping, + _Out_opt_ PUNICODE_STRING Remainder, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ContainsDevice, + _Out_ PNC_PATH_OVERLAP Overlap + ); + +_Post_satisfies_(NewName->MaximumLength < MAXUSHORT) +_Post_satisfies_(NewName->Length <= NewName->MaximumLength) +_Must_inspect_result_ +NTSTATUS +NcConstructPath ( + _In_ PNC_MAPPING_ENTRY RealPath, + _In_ PUNICODE_STRING Remainder, + _In_ BOOLEAN IncludeVolume, + _Out_ _At_(NewName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING NewName + ); + +NTSTATUS +NcParseFinalComponent ( + _In_ PUNICODE_STRING EntirePath, + _Out_ PUNICODE_STRING ParentPath, + _Out_ PUNICODE_STRING FinalComponent + ); + +// +// The following functions exist in nccontext.c +// + +NTSTATUS +NcStreamHandleContextAllocAndAttach( + _In_ PFLT_FILTER Filter, + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PNC_STREAM_HANDLE_CONTEXT * Context + ); + +VOID +NcInstanceContextClose ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +NcStreamHandleContextClose ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +// +// The following functions exist in nccreate.c +// + +FLT_PREOP_CALLBACK_STATUS +NcPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// The following functions exist in ncnameprov.c +// + +NTSTATUS +NcGenerateFileName ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL FileName + ); + +NTSTATUS +NcNormalizeNameComponentEx ( + _In_ PFLT_INSTANCE Instance, + _In_opt_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); + +// +// The following functions exist in ncoffsets.c +// + +BOOLEAN +NcDetermineStructureOffsets ( + _Out_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ FILE_INFORMATION_CLASS Information + ); + +VOID +NcCalculateDirectoryNotificationOffsets ( + PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +ULONG +NcGetNextEntryOffset ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +PVOID +NcGetNextEntry ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +ULONG +NcGetFileNameLength ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +ULONG +NcGetEntrySize ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +PWSTR +NcGetFileName ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +PWSTR +NcGetShortName ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +CCHAR +NcGetShortNameLength ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +VOID +NcSetNextEntryOffset ( + _Inout_ PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ BOOLEAN ForceLast); + +VOID +NcSetFileName ( + _In_ PVOID Entry, + _In_ PWSTR NewName, + _In_ ULONG Length, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ BOOLEAN ForceLast + ); + +VOID +NcSetShortName ( + _In_ PVOID Entry, + _In_ PWSTR NewShortName, + _In_ USHORT Length, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ); + +// +// The following functions exist in ncdirenum.c +// + +FLT_PREOP_CALLBACK_STATUS +NcEnumerateDirectory ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +NTSTATUS +NcEnumerateDirectorySetupInjection ( + _Inout_ PNC_DIR_QRY_CONTEXT DirQryCtx, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ FILE_INFORMATION_CLASS InformationClass + ); + +VOID +NcEnumerateDirectoryReset ( + _Inout_ PNC_DIR_QRY_CONTEXT dirCtx + ); + +NTSTATUS +NcPerformEnumeration(); + +NTSTATUS +NcPopulateCacheEntry ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ ULONG BufferLength, + _In_ FILE_INFORMATION_CLASS FileInfoClass, + _In_ PUNICODE_STRING SearchString, + _In_ BOOLEAN RestartScan, + _Out_ PNC_CACHE_ENTRY Cache + ); + +PNC_CACHE_ENTRY +NcDirEnumSelectNextEntry ( + _Inout_ PNC_DIR_QRY_CONTEXT Context, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ BOOLEAN IgnoreCase + ); + +BOOLEAN +NcSkipName ( + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ PNC_DIR_QRY_CONTEXT Context, + _In_ NC_PATH_OVERLAP RealOverlap, + _In_ PNC_MAPPING Mapping, + _In_ BOOLEAN IgnoreCase + ); + +_Success_(*Copied) +ULONG +NcCopyDirEnumEntry ( + _Out_ PVOID UserBuffer, + _In_ ULONG UserOffset, + _In_ ULONG UserSize, + _Inout_ PNC_CACHE_ENTRY Entry, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _Out_ PBOOLEAN Copied + ); + +NTSTATUS +NcStreamHandleContextDirEnumCreate ( + _Out_ PNC_DIR_QRY_CONTEXT Context + ); + +NTSTATUS +NcStreamHandleContextEnumSetup ( + _Inout_ PNC_DIR_QRY_CONTEXT DirContext, + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ NC_PATH_OVERLAP UserMappingOverlap, + _Out_ PBOOLEAN FirstUsage + ); + +VOID +NcStreamHandleContextEnumClose ( + _In_ PNC_DIR_QRY_CONTEXT DirContext + ); + +// +// The following functions exist in ncdirnotify.c +// + +FLT_PREOP_CALLBACK_STATUS +NcPreNotifyDirectory ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostNotifyDirectory ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +NTSTATUS +NcStreamHandleContextNotCreate ( + _Out_ PNC_DIR_NOT_CONTEXT Context + ); + +VOID +NcStreamHandleContextNotCleanup( + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext + ); + +VOID +NcStreamHandleContextNotClose ( + _In_ PNC_DIR_NOT_CONTEXT DirContext + ); + +// +// The following functions exist in ncfileinfo.c +// + +FLT_PREOP_CALLBACK_STATUS +NcPreQueryAlternateName ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostQueryHardLinks ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostQueryName ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreRename ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreSetDisposition ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreSetLinkInformation ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreSetShortName ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// The following functions exist in ncfsctrl.c +// + + +FLT_POSTOP_CALLBACK_STATUS +NcPostEnumUsnData ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +NTSTATUS +NcStreamHandleContextFindBySidCreate ( + _Out_ PNC_FIND_BY_SID_CONTEXT Context + ); + +VOID +NcStreamHandleContextFindBySidClose ( + _In_ PNC_FIND_BY_SID_CONTEXT Context + ); + +FLT_PREOP_CALLBACK_STATUS +NcPreFindFilesBySid ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostFindFilesBySid ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +#ifdef FLT_MGR_WIN7 +FLT_POSTOP_CALLBACK_STATUS +NcPostLookupStreamFromCluster ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); +#endif + +FLT_POSTOP_CALLBACK_STATUS +NcPostReadFileUsnData ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostReadUsnJournal ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +// +// The following functions exist in nccompat.c +// + +VOID +NcCompatInit(); + +// +// Global variables +// + +extern PFLT_FILTER gFilterHandle; +extern NC_REPLACE_FILEOBJECT_NAME_TYPE NcReplaceFileObjectName; +extern NC_QUERY_DIRECTORY_FILE_TYPE NcQueryDirectoryFile; +extern NC_CREATE_FILE_EX2_TYPE NcCreateFileEx2; +extern NC_GET_NEW_SYSTEM_BUFFER_ADDRESS NcGetNewSystemBufferAddress; + +#endif /* __NC_H__ */ + diff --git a/filesys/miniFilter/NameChanger/nc.rc b/filesys/miniFilter/NameChanger/nc.rc new file mode 100644 index 00000000..490b44cb --- /dev/null +++ b/filesys/miniFilter/NameChanger/nc.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Name Changer Filter Driver" +#define VER_INTERNALNAME_STR "nc.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/NameChanger/nccompat.c b/filesys/miniFilter/NameChanger/nccompat.c new file mode 100644 index 00000000..204b10fe --- /dev/null +++ b/filesys/miniFilter/NameChanger/nccompat.c @@ -0,0 +1,468 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + nccompat.c + +Abstract: + + Contains compatibility routines so that name changer works on + all OSes. Functions not available on older OSes are emulated here. + We try, wherever possible, to call into newer routines directly and + only fall back to emulation if required. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +// +// Prototypes for alternates. +// + +NTSTATUS +NcReplaceFileObjectNameAlternate ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ); + +NTSTATUS +NcQueryDirectoryFileAlternate ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(Length) PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ); + +NTSTATUS +FLTAPI +NcCreateFileEx2Alternate ( + _In_ PFLT_FILTER Filter, + _In_opt_ PFLT_INSTANCE Instance, + _Out_ PHANDLE FileHandle, + _Outptr_opt_ PFILE_OBJECT *FileObject, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, + _In_ ULONG EaLength, + _In_ ULONG Flags, + _In_opt_ PIO_DRIVER_CREATE_CONTEXT DriverContext + ); + +#pragma alloc_text(PAGE, NcCompatInit) +#pragma alloc_text(PAGE, NcCreateFileEx2Alternate) +#pragma alloc_text(PAGE, NcQueryDirectoryFileAlternate) +#pragma alloc_text(PAGE, NcReplaceFileObjectNameAlternate) + +// +// Global variables which hold pointers to the functions we will call. +// + +NC_REPLACE_FILEOBJECT_NAME_TYPE NcReplaceFileObjectName; +NC_QUERY_DIRECTORY_FILE_TYPE NcQueryDirectoryFile; +NC_CREATE_FILE_EX2_TYPE NcCreateFileEx2; +NC_GET_NEW_SYSTEM_BUFFER_ADDRESS NcGetNewSystemBufferAddress; + +// +// Holder for FltCreateFileEx, if it exists and we need to call it. +// + +NC_CREATE_FILE_EX_TYPE NcCreateFileEx; + +// +// Alternative routines which are used when on downlevel os'es. +// + +NTSTATUS +NcReplaceFileObjectNameAlternate ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ) +/*++ + +Routine Description: + + This routine is used to replace a file object's name with a provided + name. On Win7 and forward IoReplaceFileObjectName will be used. + If this function is used and verifier is enabled on pre Win7 machines + the filter will fail to unload due to a false positive on the leaked + pool test. + +Arguments: + + FileObject - Pointer to file object whose name is to be replaced. + + NewFileName - Pointer to buffer containing the new name. + + FileNameLength - Length of the new name in bytes. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES - No memory to allocate the new buffer. + + STATUS_SUCCESS otherwise. + +--*/ +{ + PWSTR Buffer; + PUNICODE_STRING FileName; + USHORT NewMaxLength; + + PAGED_CODE(); + + FileName = &FileObject->FileName; + + // + // If the new name fits inside the current buffer we simply copy it over + // instead of allocating a new buffer (and keep the MaximumLength value + // the same). + // + + if (FileNameLength <= FileName->MaximumLength) { + + goto CopyAndReturn; + } + + NewMaxLength = FileNameLength; + + Buffer = ExAllocatePoolWithTag( PagedPool, + NewMaxLength, + NC_FILE_NAME_TAG ); + + if (!Buffer) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (FileName->Buffer != NULL) { + + ExFreePool(FileName->Buffer); + } + + FileName->Buffer = Buffer; + FileName->MaximumLength = NewMaxLength; + +CopyAndReturn: + + RtlZeroMemory(FileName->Buffer, FileName->MaximumLength); + + FileName->Length = FileNameLength; + RtlCopyMemory(FileName->Buffer, NewFileName, FileNameLength); + + return STATUS_SUCCESS; +} + +NTSTATUS +NcQueryDirectoryFileAlternate ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(Length) PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ) +/*++ + +Routine Description: + + This function issues a directory control operation to the filesystem + +Arguments: + + + Instance - Supplies the Instance initiating this IO. + + FileObject - Supplies the file object about which the requested + Information should be queried. + + FileInformation - Supplies a buffer to hold the directory control + results. + + Length - Supplies the length, in bytes, of the FileInformation buffer. + + FileInformationClass - Specifies the file Information class requested. + + ReturnSingleEntry - Retrieve only one file at a time. + + FileName - This is the pattern to search for. + + RestartScan - Restart the directory enumeration from the start. + + LengthReturned - On success, this is how much Information was placed + into the FileInformation buffer. + +Return Value: + + The status returned is the final completion Status of the operation. + +--*/ +{ + PFLT_CALLBACK_DATA Info; + NTSTATUS Status; + + PAGED_CODE(); + + Status = FltAllocateCallbackData( Instance, FileObject, &Info ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + Info->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; + Info->Iopb->MinorFunction = IRP_MN_QUERY_DIRECTORY; + + Info->Iopb->Parameters.DirectoryControl.QueryDirectory.Length = Length; + Info->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName = FileName; + Info->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass = FileInformationClass; + Info->Iopb->Parameters.DirectoryControl.QueryDirectory.FileIndex = 0; + + Info->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer = FileInformation; + Info->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress = NULL; + + if (RestartScan) { + + Info->Iopb->OperationFlags |= SL_RESTART_SCAN; + } + + if (ReturnSingleEntry) { + + Info->Iopb->OperationFlags |= SL_RETURN_SINGLE_ENTRY; + } + + // + // Perform the operation + // + + FltPerformSynchronousIo( Info ); + + Status = Info->IoStatus.Status; + + if (ARGUMENT_PRESENT(LengthReturned) && + NT_SUCCESS( Status )) { + + *LengthReturned = (ULONG) Info->IoStatus.Information; + } + + FltFreeCallbackData( Info ); + + return Status; +} + +NTSTATUS +FLTAPI +NcCreateFileEx2Alternate ( + _In_ PFLT_FILTER Filter, + _In_opt_ PFLT_INSTANCE Instance, + _Out_ PHANDLE FileHandle, + _Outptr_opt_ PFILE_OBJECT *FileObject, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, + _In_ ULONG EaLength, + _In_ ULONG Flags, + _In_opt_ PIO_DRIVER_CREATE_CONTEXT DriverContext + ) +{ + NTSTATUS Status; + + PAGED_CODE(); + + // + // If we are here, FltCreateFileEx2 does not exist. We + // cannot open files within the context of a transaction + // from here. If Txf exists, so should FltCreateFileEx2. + // + + UNREFERENCED_PARAMETER( DriverContext ); + FLT_ASSERT( DriverContext == NULL ); + + // + // Zero out output parameters. + // + + *FileHandle = INVALID_HANDLE_VALUE; + + if (ARGUMENT_PRESENT( FileObject )) { + + *FileObject = NULL; + } + + if (NcCreateFileEx) { + + // + // If the system has FltCreateFileEx, we call that. + // + + Status = NcCreateFileEx( Filter, + Instance, + FileHandle, + FileObject, + DesiredAccess, + ObjectAttributes, + IoStatusBlock, + AllocationSize, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + EaBuffer, + EaLength, + Flags ); + } else { + + // + // Attempt the open. + // + + Status = FltCreateFile( Filter, + Instance, + FileHandle, + DesiredAccess, + ObjectAttributes, + IoStatusBlock, + AllocationSize, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + EaBuffer, + EaLength, + Flags ); + + if(!NT_SUCCESS( Status )) { + + goto NcCreateFileEx2Cleanup; + } + + FLT_ASSERT( *FileHandle != INVALID_HANDLE_VALUE && *FileHandle != NULL ); + + if(ARGUMENT_PRESENT( FileObject )) { + + // + // If the user provided an output FileObject parameter, + // then we need to get a reference to the fileobject and return it. + // + + Status = ObReferenceObjectByHandle( *FileHandle, + DesiredAccess, + *IoFileObjectType, + KernelMode, + FileObject, + NULL ); + + if(!NT_SUCCESS( Status )) { + + goto NcCreateFileEx2Cleanup; + } + } + } + +NcCreateFileEx2Cleanup: + + if (!NT_SUCCESS( Status )) { + + if (*FileHandle != INVALID_HANDLE_VALUE) { + + FltClose( *FileHandle ); + } + + if (ARGUMENT_PRESENT( FileObject )) { + + if (*FileObject != NULL) { + + ObDereferenceObject( *FileObject ); + } + } + } + + return Status; +} + +// +// Helper routines which manage importing the functions. +// + +VOID +NcCompatInit( ) +{ + UNICODE_STRING FuncName; + PAGED_CODE(); + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Cast from data pointer to function pointer. This is + // somewhat unavoidable when calling MmGetSystemRoutineAddress/ + // FltGetRoutineAddress etc. + // + +#pragma warning( push ) +#pragma warning( disable: 4055 ) + + RtlInitUnicodeString( &FuncName, L"IoReplaceFileObjectName" ); + + NcReplaceFileObjectName = (NC_REPLACE_FILEOBJECT_NAME_TYPE) + MmGetSystemRoutineAddress( &FuncName ); + + if (NcReplaceFileObjectName == NULL) { + + NcReplaceFileObjectName = NcReplaceFileObjectNameAlternate; + } + + NcQueryDirectoryFile = (NC_QUERY_DIRECTORY_FILE_TYPE) + FltGetRoutineAddress( "FltQueryDirectoryFile" ); + + if (NcQueryDirectoryFile == NULL) { + + NcQueryDirectoryFile = NcQueryDirectoryFileAlternate; + } + + NcCreateFileEx2 = (NC_CREATE_FILE_EX2_TYPE) + FltGetRoutineAddress( "FltCreateFileEx2" ); + + if (NcCreateFileEx2 == NULL) { + + NcCreateFileEx = (NC_CREATE_FILE_EX_TYPE) + FltGetRoutineAddress( "FltCreateFileEx" ); + NcCreateFileEx2 = NcCreateFileEx2Alternate; + } + + NcGetNewSystemBufferAddress = (NC_GET_NEW_SYSTEM_BUFFER_ADDRESS) + FltGetRoutineAddress( "FltGetNewSystemBufferAddress" ); + +#pragma warning( pop ) +} + + diff --git a/filesys/miniFilter/NameChanger/nccontext.c b/filesys/miniFilter/NameChanger/nccontext.c new file mode 100644 index 00000000..43592ecb --- /dev/null +++ b/filesys/miniFilter/NameChanger/nccontext.c @@ -0,0 +1,304 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + nccontext.c + +Abstract: + + Contains routines to manage the lifetime of instance contexts and + stream handle contexts. This file performs context-wide setup + and teardown, with callouts for specific functions to perform their + own setup and teardown. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcInstanceContextClose) +#pragma alloc_text(PAGE, NcStreamHandleContextAllocAndAttach) +#pragma alloc_text(PAGE, NcStreamHandleContextClose) +#endif + +VOID +NcInstanceContextClose ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine is used to tear down and remove an instance context. + +Arguments: + + Context - The context to destroy. + + ContextType - The type of the context. Since this function is specific + to instance contexts, we expect this to always be the instance + context type. + +Return Value: + + None. + +--*/ +{ + PNC_INSTANCE_CONTEXT InstanceContext = (PNC_INSTANCE_CONTEXT) Context; + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + FLT_ASSERT( ContextType == FLT_INSTANCE_CONTEXT ); + + NcTeardownMapping( &InstanceContext->Mapping ); +} + +VOID +NcStreamHandleContextClose ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine is used to tear down and remove a stream handle context. + +Arguments: + + Context - The context to destroy. + + ContextType - The type of the context. Since this function is specific + to stream handle contexts, we expect this to always be the stream + handle context type. + +Return Value: + + None. + +--*/ +{ + PNC_STREAM_HANDLE_CONTEXT StreamContext = (PNC_STREAM_HANDLE_CONTEXT) Context; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + FLT_ASSERT( ContextType == FLT_STREAMHANDLE_CONTEXT ); + + // + // Clean up notification context + // + + NcStreamHandleContextEnumClose( &StreamContext->DirectoryQueryContext ); + + // + // Clean up enumeration context + // + + NcStreamHandleContextNotClose( &StreamContext->DirectoryNotificationContext ); + + // + // Clean up find by SID context + // + + NcStreamHandleContextFindBySidClose( &StreamContext->FindBySidContext ); + + // + // Clean up shared context information + // + + if (StreamContext->Lock != NULL) { + + NcFreeEResource( StreamContext->Lock ); + StreamContext->Lock = NULL; + } +} + +NTSTATUS +NcStreamHandleContextAllocAndAttach ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PNC_STREAM_HANDLE_CONTEXT * Context + ) +/*++ + +Routine Description: + + Allocates and initializes an empty stream handle context. + +Arguments: + + Filter - The filter we are going to attach with. + + Instance - The instance we are going to attach with. + + FileObject - File object we are going to attach to. + + Context - Pointer to a user allocated PNC_STREAM_HANDLE_CONTEXT. + +Return Value: + + On success, returns STATUS_SUCCESS and a pointer to the context in *Context. + Otherwise returns an error and NULL in *Context. + + The user must call FltReleaseContext to balance ref count. + +--*/ +{ + PNC_STREAM_HANDLE_CONTEXT OurContext = NULL; + PNC_STREAM_HANDLE_CONTEXT TheirContext = NULL; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // Default to no return context. + // + + *Context = NULL; + + // + // Check if a context already exists. If so, return it. + // + + Status = FltGetStreamHandleContext( Instance, + FileObject, + &TheirContext ); + + if (NT_SUCCESS( Status )) { + + *Context = TheirContext; + return Status; + } + + // + // Allocate a new context. + // + + Status = FltAllocateContext( Filter, + FLT_STREAMHANDLE_CONTEXT, + sizeof(NC_STREAM_HANDLE_CONTEXT), + PagedPool, + &OurContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextAllocAndAttachClose; + } + + // + // Zero out the buffer so we can unwind on failure. + // + + RtlZeroMemory( OurContext, sizeof(NC_STREAM_HANDLE_CONTEXT)); + + // + // Initialize shared context information. + // + + Status = NcAllocateEResource( &OurContext->Lock ); + + if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextAllocAndAttachClose; + } + + // + // Initialize the Directory Notification Structure. + // + + Status = NcStreamHandleContextNotCreate( &OurContext->DirectoryNotificationContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextAllocAndAttachClose; + } + + // + // Initialize the Directory Enumeration Structure + // + + Status = NcStreamHandleContextDirEnumCreate( &OurContext->DirectoryQueryContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextAllocAndAttachClose; + } + + // + // Initialize the Find by SID Structure + // + + Status = NcStreamHandleContextFindBySidCreate( &OurContext->FindBySidContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextAllocAndAttachClose; + } + + // + // Now we have a new context, but we could be racing with another thread + // attaching a context to this stream. We will now try to attach, but + // keep the existing context if it is there. If there is an existing + // context, then we return the pre-existing context and free ours. + // + + Status = FltSetStreamHandleContext( Instance, + FileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + OurContext, + &TheirContext ); + + if (Status == STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + Status = STATUS_SUCCESS; + + } else if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextAllocAndAttachClose; + } + + // + // We need to keep 1 ref count on the context we are sticking with. + // However, if there are two contexts we need to free one of them. + // We therefore NULL out the context that is being returned so as + // to only clean up the other (if it exists.) + // + + if (TheirContext != NULL) { + + *Context = TheirContext; + TheirContext = NULL; + + } else { + + *Context = OurContext; + OurContext = NULL; + } + +NcStreamHandleContextAllocAndAttachClose: + + if (OurContext != NULL) { + + FltReleaseContext( OurContext ); + } + + if (TheirContext != NULL) { + + FltReleaseContext( TheirContext ); + } + + return Status; +} + + diff --git a/filesys/miniFilter/NameChanger/nccreate.c b/filesys/miniFilter/NameChanger/nccreate.c new file mode 100644 index 00000000..574d64c7 --- /dev/null +++ b/filesys/miniFilter/NameChanger/nccreate.c @@ -0,0 +1,451 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + nccreate.c + +Abstract: + + Contains routines to process user-initiated creates. We need to + detect if these need to be redirected to a new physical location, + and redirect as appropriate. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcPreCreate) +#endif + + +FLT_PREOP_CALLBACK_STATUS +NcPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Routine is invoked when a create is issued by a user. + + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +Logic: + + 1) If file is opened by ID or is a Paging File, etc. + A) return FLT_PREOP_SUCCESS_NO_CALLBACK + 2) Get the opened name and calculate overlap + 3) If File is Delete On close + A) If the file is an ancestor of either mapping + a) Fail Open + 4) If the path is in the real mapping + A) Fail the Open + 5) If the path is in user mapping + A) Recalculate the full name (without SL_OPEN_TARGET_DIRECTORY) + B) Munge Name to Real Mapping + C) Switch name + D) Passthrough with callback. + 6) Else + A) Return Passthrough No callback +--*/ +{ + + + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; + PFLT_FILE_NAME_INFORMATION FullFileNameInformation = NULL; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NC_PATH_OVERLAP RealOverlap, UserOverlap, FullOverlap; + UNICODE_STRING Remainder; + UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; + BOOLEAN IgnoreCase = !BooleanFlagOn( Data->Iopb->OperationFlags, SL_CASE_SENSITIVE ); + UCHAR CreateDisposition = (UCHAR)(Data->Iopb->Parameters.Create.Options >> 24); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( CompletionContext ); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // Check if this open is to the paging file. + // We are not going to handle munging the namespace for paging files. + // + + if (FlagOn( Data->Iopb->OperationFlags, SL_OPEN_PAGING_FILE )) { + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreCreateCleanup; + } + + // + // Check if this is a volume open. + // Volume opens do not affect the namespace of the volume so we don't care. + // + + if (FlagOn( Data->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) { + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreCreateCleanup; + } + + // + // Check if this open is by ID. + // Opens by file ID are name agnostic. Thus we do not care about this open. + // + + if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) { + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreCreateCleanup; + } + + // + // Get opened name. + // This is pre-create so this is the name which will be opened if allowed + // to continue. Note that unlike most other paths, we do not call + // ourselves here; doing so would munge the "real" name into the "user" + // name, which would prevent us from detecting access via the "real" + // name. Because this request is not being sent to us, we need to ensure + // we don't poison the cache of below filters (allowing the user visible + // mapping to be cached beneath us.) + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_DO_NOT_CACHE, + &FileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + Status = FltParseFileNameInformation( FileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + // + // Get the instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + FLT_ASSERT( InstanceContext != NULL ); + + // + // Get the open path's relation to the mapping paths. + // + + NcComparePath( &FileNameInformation->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + NcComparePath( &FileNameInformation->Name, + &InstanceContext->Mapping.UserMapping, + &Remainder, + IgnoreCase, + TRUE, + &UserOverlap ); + + // + // Name changer uses the opened name of a file as an indicator that the + // file has been munged. Because of this we have to deny opens down the + // real mapping in order to disambiguate whether it was opened via the + // munging operation. We thus disallow opens down the real mapping path. + // + + if (RealOverlap.Match) { + + // + // When opening the mapping itself, we have virtualized its + // nonexistence to the caller. Therefore, any attempt to open + // should return a not found code, and any conditional open/create + // is effectively a create and should be failed with a code + // appropriate for that. + // + + ReturnValue = FLT_PREOP_COMPLETE; + switch (CreateDisposition) { + case FILE_OPEN: + case FILE_OVERWRITE: + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto NcPreCreateCleanup; + case FILE_CREATE: + case FILE_OPEN_IF: + case FILE_OVERWRITE_IF: + case FILE_SUPERSEDE: + Status = STATUS_ACCESS_DENIED; + goto NcPreCreateCleanup; + } + + FLT_ASSERT( FALSE ); + Status = STATUS_ACCESS_DENIED; + goto NcPreCreateCleanup; + + } else if (RealOverlap.InMapping) { + + Status = STATUS_OBJECT_PATH_NOT_FOUND; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + // + // Short names around the mapping cause a problem for namechanger. + // This is because the filesystem will generate new shortnames which are + // not told to fltmgr. Finding out the names accurately is not reliable + // because the name can change while fltmgr is querying the name. + // + // One solution to this problem is to protect the mapping. We prevent + // users from renaming or deleting the mapping paths so that we know the + // short names do not change. + // + // In create we need to make sure that the FILE_DELETE_ON_CLOSE flag is + // not set if the user is opening the real mapping or an ancestor of it. + // + + if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE )) { + + if (RealOverlap.Ancestor || UserOverlap.Ancestor) { + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + } + + // + // Name changer munges opens which occur down the user mapping path. + // Here we check to see if this is a name we want to change. + // + + if (UserOverlap.InMapping) { + + // + // This open is targeted inside the user mapping. + // This means that we need to munge the open name + // to the real mapping. + // + + // + // Constructing a new path for the file has a quirk because of + // SL_OPEN_TARGET_DIRECTORY. If it is set, then the name has the + // following components: /device/ancestors/parent/final_component. + // If the flag is set, then fltmgr stripped of the final component + // (leaving /device/ancestors/parent) when we called + // FltGetFileNameInformation. + // + // File systems assume that file objects for use as a target directory + // in rename operations are opened with SL_OPEN_TARGET_DIRECTORY. + // This means that unlike the related file object, which can be just + // cleared, opens with SL_OPEN_TARGET_DIRECTORY need to be propogated + // with the flag always. + // + // This means that opens which get munged and have + // SL_OPEN_TARGET_DIRECTORY need to be calculated with the final + // component in the name string. + // + + if (FlagOn( Data->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY )) { + + // + // Clear the flag so we can get the full name. + // + + ClearFlag( Data->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ); + + // + // Get the name with the final component. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY, + &FullFileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + Status = FltParseFileNameInformation( FullFileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + FLT_ASSERT( FullFileNameInformation != NULL ); + + // + // Now that we have the full path, lets put the + // flag back on the file object. + // + + SetFlag( Data->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ); + + // + // We need to re-calculate the Remainder so we can + // construct the new name. + // + + NcComparePath( &FullFileNameInformation->Name, + &InstanceContext->Mapping.UserMapping, + &Remainder, + IgnoreCase, + TRUE, + &FullOverlap ); + + // + // Since we were dealing with a path further down + // the mapping, lets make sure that we are now + // even further down the mapping. + // + + FLT_ASSERT( FullOverlap.InMapping ); + + } + + // + // Generate new name + // + + Status = NcConstructPath( &InstanceContext->Mapping.RealMapping, + &Remainder, + FALSE, + &MungedName ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + // + // Replace open name in the file object. + // + + Status = NcReplaceFileObjectName( Data->Iopb->TargetFileObject, + MungedName.Buffer, + MungedName.Length ); + + if (!NT_SUCCESS(Status)) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreCreateCleanup; + } + + // + // Because we calculate our own full path, the + // RelatedFileObject is not needed on munged creates. + // Make sure that we don't send it below us. + // + + FltObjects->FileObject->RelatedFileObject = NULL; + + // + // Pass the create below us. + // + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreCreateCleanup; + + } //end if for in mapping + + // + // If we got here then we are not interested in this open. + // Passthrough no callback. + // + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreCreateCleanup; + +NcPreCreateCleanup: + + // + // Check if we care going to fail the open. + // + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + // + // If we get here, then Status must be a failure code. + // + + FLT_ASSERT( !NT_SUCCESS( Status ) ); + + Data->IoStatus.Status = Status; + } + + // + // Clean up variables + // + + if (FileNameInformation != NULL) { + + FltReleaseFileNameInformation( FileNameInformation ); + } + + if (FullFileNameInformation != NULL) { + + FltReleaseFileNameInformation( FullFileNameInformation ); + } + + if (MungedName.Buffer != NULL) { + + ExFreePoolWithTag( MungedName.Buffer, NC_TAG ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + return ReturnValue; +} + diff --git a/filesys/miniFilter/NameChanger/ncdirenum.c b/filesys/miniFilter/NameChanger/ncdirenum.c new file mode 100644 index 00000000..7594894f --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncdirenum.c @@ -0,0 +1,1420 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + ncdirenum.c + +Abstract: + + Contains routines to process user-initiated directory enumerations. + Depending on path, we may need to suppress the real mapping from + being visible to the user, or "inject" the user mapping for the + user. We must take care to do so having regard for the pattern + matching and case sensitivity dictated by the caller. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcCopyDirEnumEntry) +#pragma alloc_text(PAGE, NcDirEnumSelectNextEntry) +#pragma alloc_text(PAGE, NcEnumerateDirectory) +#pragma alloc_text(PAGE, NcEnumerateDirectorySetupInjection) +#pragma alloc_text(PAGE, NcEnumerateDirectoryReset) +#pragma alloc_text(PAGE, NcPopulateCacheEntry) +#pragma alloc_text(PAGE, NcSkipName) +#pragma alloc_text(PAGE, NcStreamHandleContextEnumClose) +#pragma alloc_text(PAGE, NcStreamHandleContextEnumSetup) +#pragma alloc_text(PAGE, NcStreamHandleContextDirEnumCreate) +#endif + +FLT_PREOP_CALLBACK_STATUS +NcEnumerateDirectory ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Routine is invoked when a directory enumeration is issued by the + user. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + //TODO WE SHOULD CONSIDER MOVING THIS TO POST BECAUSE NTFS WILL TAKE CARE + // OF SYNC. + FLT_PREOP_CALLBACK_STATUS ReturnValue; + NTSTATUS Status; + + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; + PNC_DIR_QRY_CONTEXT DirCtx = NULL; + + PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; + + NC_PATH_OVERLAP RealOverlap; + NC_PATH_OVERLAP UserOverlap; + + BOOLEAN Reset = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RESTART_SCAN ); + BOOLEAN FirstQuery; + BOOLEAN Single = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RETURN_SINGLE_ENTRY ); + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + FILE_INFORMATION_CLASS InformationClass = + Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass; + + PVOID UserBuffer; + ULONG BufferSize; //size for user and system buffers. + + BOOLEAN Unlock = FALSE; + + //Vars for moving data into user buffer. + ULONG NumEntriesCopied; + ULONG UserBufferOffset; + ULONG LastEntryStart; + BOOLEAN MoreRoom; + PNC_CACHE_ENTRY NextEntry; + + DIRECTORY_CONTROL_OFFSETS Offsets; + + BOOLEAN FoundStructureOffsets; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + FoundStructureOffsets = NcDetermineStructureOffsets( &Offsets, + InformationClass ); + + if (!FoundStructureOffsets) { + + Status = STATUS_INVALID_PARAMETER; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + // + // Get our instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + // + // Get the directory's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, + &FileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + Status = FltParseFileNameInformation( FileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + // + // See if the directory is parent of either mapping. + // + + NcComparePath( &FileNameInformation->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + NcComparePath( &FileNameInformation->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + if (!(UserOverlap.Parent || RealOverlap.Parent )) { + + // + // We are not interested in this directory + // because it is not the parent of either + // mapping. This means we can just passthrough. + // + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcEnumerateDirectoryCleanup; + } + + Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, + FltObjects->Instance, + FltObjects->FileObject, + &HandleContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + FLT_ASSERT( HandleContext != NULL ); + + DirCtx = &HandleContext->DirectoryQueryContext; + _Analysis_assume_( DirCtx != NULL ); + + // + // Before looking at the context, we have to acquire the lock. + // + + NcLockStreamHandleContext( HandleContext ); + Unlock = TRUE; + + // + // We don't allow multiple outstanding enumeration requests on + // a single handle. + // + // TODO: This needs to change. + // + + if (DirCtx->EnumerationOutstanding) { + + Status = STATUS_UNSUCCESSFUL; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + + } + + DirCtx->EnumerationOutstanding = TRUE; + + // + // Now drop the lock. We're protected by the EnumerationOutstanding + // flag; nobody else can muck with the enumeration context structure. + // + + NcUnlockStreamHandleContext( HandleContext ); + Unlock = FALSE; + + // + // Now we need to initialize or clear the cache and query options. + // + + Status = NcStreamHandleContextEnumSetup( DirCtx, + InstanceContext, + &Offsets, + Data, + FltObjects, + UserOverlap, + &FirstQuery ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + // + // Prepare to populate the user buffer. + // + + UserBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; + + BufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length; + + // + // Lets copy data into the user buffer. + // + + NumEntriesCopied = 0; + UserBufferOffset = 0; + + do { + + // + // If there is no cache entry, populate it. + // + + if (DirCtx->Cache.Buffer == NULL) { + + Status = NcPopulateCacheEntry( FltObjects->Instance, + FltObjects->FileObject, + Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length, + Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass, + Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName, + Reset, + &DirCtx->Cache); + + // + // We only want to reset the cache once. + // + + Reset = FALSE; + + // + // There was a problem populating cache, pass up to user. + // + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + } + + NextEntry = NcDirEnumSelectNextEntry( DirCtx, &Offsets, IgnoreCase ); + + if (NextEntry == NULL) { + + // + // There are no more entries. + // + + break; + } + + if (NcSkipName( &Offsets, + DirCtx, + RealOverlap, + &InstanceContext->Mapping, + IgnoreCase )) { + + // + // This entry is the real mapping path. That means we have to mask it... + // We will say there is more room and continue. + // + + MoreRoom = TRUE; + + } else { + + // + // We are keeping this entry! + // + + try { + + LastEntryStart = UserBufferOffset; + UserBufferOffset = NcCopyDirEnumEntry( UserBuffer, + UserBufferOffset, + BufferSize, + NextEntry, + &Offsets, + &MoreRoom ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + + if (MoreRoom) { + + NumEntriesCopied++; + } + + }// end of "we are copying entry" + + } while (MoreRoom && + (Single ? (NumEntriesCopied < 1) : TRUE)); + + if (NumEntriesCopied > 0) { + + // + // Now we know what the last entry in the user buffer is going to be. + // Set its NextEntryOffset to 0, so that the user knows its the last element. + // + + try { + + NcSetNextEntryOffset( Add2Ptr(UserBuffer, LastEntryStart), + &Offsets, + TRUE ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + } + } + + // + // We finished copying data. + // + + ReturnValue = FLT_PREOP_COMPLETE; + + if (NumEntriesCopied == 0) { + + if (FirstQuery) { + + Status = STATUS_NO_SUCH_FILE; + + } else { + + Status = STATUS_NO_MORE_FILES; + } + + } else { + + Status = STATUS_SUCCESS; + } + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcEnumerateDirectoryCleanup; + +NcEnumerateDirectoryCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + // + // We need to write back results of query. + // + + Data->IoStatus.Status = Status; + + if (NT_SUCCESS( Status )) { + + //success + Data->IoStatus.Information = UserBufferOffset; + + } else { + + //failure + Data->IoStatus.Information = 0; + } + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (DirCtx != NULL) { + + if (!Unlock) { + NcLockStreamHandleContext( HandleContext ); + Unlock = TRUE; + } + + FLT_ASSERT( DirCtx->EnumerationOutstanding ); + DirCtx->EnumerationOutstanding = FALSE; + + NcUnlockStreamHandleContext( HandleContext ); + Unlock = FALSE; + + FltReleaseContext( HandleContext ); + } + + FLT_ASSERT( !Unlock ); + + if (FileNameInformation != NULL) { + + FltReleaseFileNameInformation( FileNameInformation ); + } + + return ReturnValue; +} + +NTSTATUS +NcEnumerateDirectorySetupInjection ( + _Inout_ PNC_DIR_QRY_CONTEXT DirQryCtx, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ FILE_INFORMATION_CLASS InformationClass + ) +/*++ + +Routine Description: + + Sets up directory enumeration context cache so that we are ready to + perform injection. + +Arguments: + + DirQryCtx - Pointer to directory query context (on the stream handle.) + + FltObjects - FltObjects structure for this operation. + + InstanceContext - Instance Context for this operation. + + Offsets - Offsets structure for this information class. + + InformationClass - The information class for this operation. + +Return Value: + + Returns STATUS_SUCCESS on success, otherwise an appropriate error code. + +--*/ +{ + NTSTATUS Status; + + OBJECT_ATTRIBUTES RealParentAttributes; + HANDLE RealParentHandle = 0; //close always + PFILE_OBJECT RealParentFileObj = NULL; + IO_STATUS_BLOCK RealParentStatusBlock; + char * QueryBuffer = NULL; //free on error, when no injection + ULONG QueryBufferLength = 0; + USHORT NameLength; + ULONG QueryBufferLengthRead; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + PAGED_CODE(); + + + // + // If the user has specified a search string, and if our user mapping + // should not be returned in this search string, return success. We + // don't need to inject anything. + // + + if (DirQryCtx->SearchString.Length > 0 && + !FsRtlIsNameInExpression( &DirQryCtx->SearchString, + &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName, + IgnoreCase, + NULL ) && + !FsRtlIsNameInExpression( &DirQryCtx->SearchString, + &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName, + IgnoreCase, + NULL )) { + + Status = STATUS_SUCCESS; + goto NcEnumerateDirectorySetupCleanup; + } + + // + // Initialize insertion info. + // + // We have to insert the final component of the real mapping + // as the final component of the user mapping. To do this we + // will open the parent of the real mapping, and query the real + // mapping. + // Then we will overwrite the real mapping's name with + // the final component of the user mapping. This data will be + // stored in the DirQryCtx for later injection. + // + + // + // Open parent of real mapping. + // + + InitializeObjectAttributes( &RealParentAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // InstanceOffsets + &RealParentHandle, // Returned Handle + &RealParentFileObj, // Returned FileObject + FILE_LIST_DIRECTORY|FILE_TRAVERSE, // Desired Access + &RealParentAttributes, // object attributes + &RealParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction state + + if (!NT_SUCCESS( Status )) { + + goto NcEnumerateDirectorySetupCleanup; + } + + // + // Allocate Buffer to store mapping data. + // + + NameLength = Max( InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName.Length, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length ); + + QueryBufferLength = Offsets->FileNameDist + NameLength; + + QueryBuffer = ExAllocatePoolWithTag( PagedPool, QueryBufferLength, NC_DIR_QRY_CACHE_TAG ); + + if (QueryBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcEnumerateDirectorySetupCleanup; + } + + // + // Query the information from the parent of the real mapping. + // + + Status = NcQueryDirectoryFile( FltObjects->Instance, + RealParentFileObj, + QueryBuffer, + QueryBufferLength, + InformationClass, + TRUE,//Return single entry + &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName, + FALSE,//restart scan + &QueryBufferLengthRead); + + if (Status == STATUS_NO_SUCH_FILE) { + + // + // The user mapping does not exist, this is allowed. It means we + // have nothing to inject. + // + + DirQryCtx->InjectionEntry.Buffer = NULL; + DirQryCtx->InjectionEntry.CurrentOffset = 0; + + ExFreePoolWithTag( QueryBuffer, NC_DIR_QRY_CACHE_TAG ); + QueryBuffer = NULL; + + Status = STATUS_SUCCESS; + + } else if (!NT_SUCCESS( Status )) { + + // + // An unexpected error occurred, return code. + // + + goto NcEnumerateDirectorySetupCleanup; + + } else { + + // + // Now we have to munge the real mapping directory entry into a + // user mapping directory entry. + // + + NcSetFileName( QueryBuffer, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Buffer, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length, + Offsets, + TRUE ); + + NcSetShortName( QueryBuffer, + InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName.Buffer, + InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName.Length, + Offsets ); + + FLT_ASSERT( DirQryCtx->InjectionEntry.Buffer == NULL ); + + // + // Set the injection entry up in the cache. + // + + DirQryCtx->InjectionEntry.Buffer = QueryBuffer; + DirQryCtx->InjectionEntry.CurrentOffset = 0; + } + +NcEnumerateDirectorySetupCleanup: + + if (!NT_SUCCESS( Status )) { + + if(QueryBuffer != NULL) { + + ExFreePoolWithTag( QueryBuffer, NC_DIR_QRY_CACHE_TAG ); + } + + } + + if (RealParentHandle != NULL) { + + FltClose( RealParentHandle ); + } + + if (RealParentFileObj != NULL) { + + ObDereferenceObject( RealParentFileObj ); + } + + return Status; +} + +VOID +NcEnumerateDirectoryReset ( + _Inout_ PNC_DIR_QRY_CONTEXT DirCtx + ) +/*++ + +Routine Description: + + Tears down any stream handle context related to enumeration and prepares + the stream handle context for reuse. + +Arguments: + + DirQryCtx - Pointer to directory query context (on the stream handle.) + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + ExFreePoolWithTag( DirCtx->Cache.Buffer, NC_DIR_QRY_CACHE_TAG ); + DirCtx->Cache.Buffer = NULL; + DirCtx->Cache.CurrentOffset = 0; + + ExFreePoolWithTag( DirCtx->InjectionEntry.Buffer, NC_DIR_QRY_CACHE_TAG ); + DirCtx->InjectionEntry.Buffer = NULL; + DirCtx->InjectionEntry.CurrentOffset = 0; +} + +BOOLEAN +NcSkipName ( + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ PNC_DIR_QRY_CONTEXT Context, + _In_ NC_PATH_OVERLAP RealOverlap, + _In_ PNC_MAPPING Mapping, + _In_ BOOLEAN IgnoreCase + ) +/*++ + +Routine Description: + + Determines if the next entry is for the real mapping. If it is, we want + to "skip" returning this entry and proceed to the next. + +Arguments: + + Offsets - Offset information for this enumeration class. + + Context - Pointer to directory query context (on the stream handle.) + This is used to obtain the entry we are contemplating returning. + + RealOverlap - Relationship of the object that enumeration is being + requested on to the real mapping. This routine will only skip + entries if we are enumerating the parent of the real mapping. + +Return Value: + + TRUE if this entry should be skipped/suppressed; FALSE if it should be + returned to the user. + +--*/ +{ + BOOLEAN Result = FALSE; + PVOID CacheEntry; + UNICODE_STRING CacheString; + PUNICODE_STRING IgnoreString = &Mapping->RealMapping.LongNamePath.FinalComponentName; + ULONG ElementSize; + BOOLEAN LastElement; + + PAGED_CODE(); + + if (RealOverlap.Parent) { + + // + // We have to check for a match. + // + + CacheEntry = Add2Ptr( Context->Cache.Buffer, Context->Cache.CurrentOffset); + ElementSize = NcGetEntrySize( CacheEntry, Offsets ); + LastElement = (BOOLEAN)(NcGetNextEntryOffset( CacheEntry, Offsets ) == 0); + + CacheString.Buffer = NcGetFileName( CacheEntry, Offsets ); + CacheString.Length = (USHORT) NcGetFileNameLength( CacheEntry, Offsets ); + CacheString.MaximumLength = CacheString.Length; + + + if (RtlCompareUnicodeString( &CacheString, + IgnoreString, + IgnoreCase ) == 0) { + + // + // We need to ignore this name. + // + + Result = TRUE; + + // + // skip + // + + if (LastElement) { + + // + // This was the last element in the entry, so we should clean the entry. + // + + ExFreePoolWithTag(Context->Cache.Buffer, NC_DIR_QRY_CACHE_TAG); + Context->Cache.Buffer = NULL; + Context->Cache.CurrentOffset = 0; + + } else { + + // + // Entry has more elements, update offset counter. + // + + Context->Cache.CurrentOffset += ElementSize; + } + } + } + + return Result; +} + +NTSTATUS +NcPopulateCacheEntry ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ ULONG BufferLength, + _In_ FILE_INFORMATION_CLASS FileInfoClass, + _In_ PUNICODE_STRING SearchString, + _In_ BOOLEAN RestartScan, + _Out_ PNC_CACHE_ENTRY Cache + ) +/*++ + +Routine Description: + + Obtains the next entry from the filesystem. By always reading ahead, we + can determine when to return the injected entry (if one exists) while + attempting to preserve directory sort order. + +Arguments: + + Instance - Instance of this filter in the filter stack. + + FileObject - Directory that we are enumerating. + + BufferLength - Size, in bytes, of the buffer to allocate within this + routine. + + FileInfoClass - Directory enumeration class that we are using. + + SearchString - Pointer to the string containing the enumeration criteria. + Will be empty if enumerating all objects in a directory. + + RestartScan - Boolean value set to TRUE if we should start enumeration from + the beginning. Set to FALSE to continue from the previous point. + + Cache - Pointer to our structure for receiving the cached entry. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + + NTSTATUS Status; + PVOID Buffer; + + PAGED_CODE(); + + if (SearchString != NULL && SearchString->Buffer == NULL) { + + // + // In the case there is no search string provided, + // don't pass one to the filesystem. + // + + SearchString = NULL; + } + + Buffer = ExAllocatePoolWithTag( PagedPool, BufferLength, NC_DIR_QRY_CACHE_TAG ); + + if (Buffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + return Status; + } + + Status = NcQueryDirectoryFile( Instance, + FileObject, + Buffer, + BufferLength, + FileInfoClass, + FALSE, + SearchString, + RestartScan, + NULL); + + if (Status == STATUS_NO_MORE_FILES || Status == STATUS_NO_SUCH_FILE) { + + // + // There are no more files. Keep cache empty. + // + + Cache->Buffer = NULL; + Cache->CurrentOffset = 0; + + ExFreePoolWithTag( Buffer, NC_DIR_QRY_CACHE_TAG ); + + Status = STATUS_SUCCESS; + + } else if (NT_SUCCESS( Status )) { + + // + // There were entries, populate. + // + + Cache->Buffer = Buffer; + Cache->CurrentOffset = 0; + + } else { + + // + // An unspecified error occurred, return it. + // + + ExFreePoolWithTag( Buffer, NC_DIR_QRY_CACHE_TAG ); + } + + return Status; +} + +PNC_CACHE_ENTRY +NcDirEnumSelectNextEntry( + _Inout_ PNC_DIR_QRY_CONTEXT Context, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ BOOLEAN IgnoreCase + ) +/*++ + +Routine Description: + + This routine determines whether the cached entry (returned from the + filesystem) should be returned now or whether the injected entry (as a + result of our mapping) should be returned now. + +Arguments: + + Context - The enumeration context of this handle. + + Offsets - Information describing the offsets for this enumeration class. + + IgnoreCase - TRUE if we are case insensitive, FALSE if case sensitive. + +Return Value: + + A pointer to the entry we should return, or NULL if there is nothing + remaining to return. + +--*/ +{ + PNC_CACHE_ENTRY NextEntry; + UNICODE_STRING CacheString; + UNICODE_STRING InsertString; + PVOID CacheEntry; + PVOID InjectEntry; + + PAGED_CODE(); + + // + // Figure out which name comes first + // + + if ((Context->Cache.Buffer == NULL) && + (Context->InjectionEntry.Buffer == NULL)) { + + // + // There are no names left, return STATUS_NO_MORE_FILES + // + + NextEntry = NULL; + + } else if (Context->Cache.Buffer == NULL) { + + // + // The cache is empty, so inject. + // + + NextEntry = &Context->InjectionEntry; + + } else if (Context->InjectionEntry.Buffer == NULL) { + + // + // The injection entry is empty, drain the cache. + // + + NextEntry = &Context->Cache; + + } else { + + // + // We have to seek in the entry buffer to the current entry within the buffer. + // + + CacheEntry = Add2Ptr( Context->Cache.Buffer, Context->Cache.CurrentOffset ); + InjectEntry = Add2Ptr( Context->InjectionEntry.Buffer, Context->InjectionEntry.CurrentOffset ); + + // + // Find names within the entries. + // + + CacheString.Buffer = NcGetFileName( CacheEntry, Offsets ); + CacheString.Length = (USHORT) NcGetFileNameLength( CacheEntry, Offsets ); + CacheString.MaximumLength = CacheString.Length; + + InsertString.Buffer = NcGetFileName( InjectEntry, Offsets ); + InsertString.Length = (USHORT) NcGetFileNameLength( InjectEntry, Offsets ); + InsertString.MaximumLength = InsertString.Length; + + // + // Compare the names + // + + if (RtlCompareUnicodeString( &CacheString, + &InsertString, + IgnoreCase ) < 0) { + + // + // Cache string comes first + // + + NextEntry = &Context->Cache; + + } else { + + // + // insert string comes first + // + + NextEntry = &Context->InjectionEntry; + } + } + return NextEntry; +} + +_Success_(*Copied) +ULONG +NcCopyDirEnumEntry ( + _Out_ PVOID UserBuffer, + _In_ ULONG UserOffset, + _In_ ULONG UserSize, + _Inout_ PNC_CACHE_ENTRY Entry, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _Out_ PBOOLEAN Copied + ) +/*++ + +Routine Description: + + This routine copies a single enumeration result into the caller's + buffer. + +Arguments: + + UserBuffer - Pointer to the caller's buffer. + + UserOffset - Offset within the caller's buffer that we intend to write new + results. + + UserSize - Size of the caller's buffer, in bytes. + + Entry - Pointer to the directory entry that we intend to return. + + Offsets - Information describing the offsets for this enumeration class. + + Copied - Pointer to a boolean value indicating whether this routine copied + a new entry or not. + +Return Value: + + The new offset in the user buffer that any future copies should use. + +--*/ +{ + PVOID Element = Add2Ptr( Entry->Buffer, Entry->CurrentOffset ); + PVOID Dest = Add2Ptr( UserBuffer, UserOffset ); + ULONG ElementSize = NcGetEntrySize( Element, Offsets ); + BOOLEAN LastElement = (BOOLEAN)(NcGetNextEntryOffset( Element, Offsets ) == 0); + + PAGED_CODE(); + + if (UserSize - UserOffset >= ElementSize) { + + // + // There is enough room for this element, so copy it. + // + + RtlCopyMemory( Dest, Element, ElementSize ); + UserOffset += ElementSize; + *Copied = TRUE; + + // + // Update Entry's Offset + // + + if (LastElement) { + + // + // This was the last element in the entry, so we should clean the + // entry. + // + + ExFreePoolWithTag( Entry->Buffer, NC_TAG ); + Entry->Buffer = NULL; + Entry->CurrentOffset = 0; + + // + // The last element in cached entries have a NextEntryOffset of 0, + // make sure that we report the actual next entry offset. + // + + NcSetNextEntryOffset( Dest, Offsets, FALSE ); + + } else { + + // + // Entry has more elements, update offset counter. + // + + Entry->CurrentOffset += ElementSize; + } + + } else { + + // + // User buffer does not have enough space. + // + + *Copied = FALSE; + } + + return UserOffset; +} + +NTSTATUS +NcStreamHandleContextDirEnumCreate ( + _Out_ PNC_DIR_QRY_CONTEXT Context + ) +/*++ + +Routine Description: + + Initializes the stream handle context ready for directory enumeration + requests. + +Arguments: + + Context - Pointer to the directory context. + +Return Value: + + The return value is the Status of the operation. Currently this operation + cannot fail, so this function always returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + Context->EnumerationOutstanding = FALSE; + Context->InUse = FALSE; + Context->Cache.Buffer = NULL; + Context->Cache.CurrentOffset = 0; + Context->InjectionEntry.Buffer = NULL; + Context->InjectionEntry.CurrentOffset = 0; + Context->SearchString.Length = 0; + Context->SearchString.MaximumLength = 0; + Context->SearchString.Buffer = NULL; + + return Status; +} + + +NTSTATUS +NcStreamHandleContextEnumSetup ( + _Inout_ PNC_DIR_QRY_CONTEXT DirContext, + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ NC_PATH_OVERLAP UserMappingOverlap, + _Out_ PBOOLEAN FirstUsage + ) +/*++ + +Routine Description: + + Acquires the stream handle context and initializes it for a directory + enumeration. + +Arguments: + + DirContext - Pointer to the directory context. + + InstanceContext - Pointer to this instance's context. + + Offsets - Offsets structure for this enumeration class. + + Data - Callback data for this operation. + + FltObjects - FltObjects structure for this operation. + + UserMappingOverlap - The overlap between the user mapping and this file + object. + + FirstUsage - Weather or not this is the first usage of this handle in a + directory enumeration. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + PUNICODE_STRING SearchString = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName; + FILE_INFORMATION_CLASS InformationClass = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass; + BOOLEAN ResetSearch = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RESTART_SCAN ); + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + PAGED_CODE(); + + // + // This context could be in its first use. If it is, then we need to + // setup the search string and information class. + // + + if (DirContext->InUse == FALSE) { + + // + // This was the first usage of the context. + // We should set up the search string. + // + + if (SearchString != NULL) { + + DirContext->SearchString.Buffer = ExAllocatePoolWithTag( PagedPool, + SearchString->Length, + NC_DIR_QRY_SEARCH_STRING ); + + if (DirContext->SearchString.Buffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcStreamHandleContextEnumSetupCleanup; + } + + DirContext->SearchString.MaximumLength = SearchString->Length; + + if (IgnoreCase) { + + RtlUpcaseUnicodeString( &DirContext->SearchString, SearchString, FALSE ); + + } else { + + RtlCopyUnicodeString( &DirContext->SearchString, SearchString ); + } + + DirContext->SearchString.Length = SearchString->Length; + + } else { + + RtlInitEmptyUnicodeString( &DirContext->SearchString, + NULL, + 0 ); + } + + DirContext->InformationClass = InformationClass; + + // + // We should write back to the caller so they know its the first usage. + // + + *FirstUsage = TRUE; + + } else { + + // + // This is not the fist usage, so write back to user. + // + + *FirstUsage = FALSE; + + // + // This is not the first query. Lets make sure that our data + // is consistent. If the information classes don't line up + // then our cache might be inconsistant. We should fail this + // operation. + // + // TODO: Is this correct? + // + + if (DirContext->InformationClass != InformationClass) { + + Status = STATUS_INVALID_PARAMETER; + goto NcStreamHandleContextEnumSetupCleanup; + } + } + + if (!DirContext->InUse || ResetSearch) { + + // + // Either this is the first use of the context, + // or they are reseting the enumeration. We + // should clear the cache either way. + // + + if (DirContext->Cache.Buffer != NULL) { + + ExFreePoolWithTag( DirContext->Cache.Buffer, NC_TAG ); + DirContext->Cache.Buffer = NULL; + DirContext->Cache.CurrentOffset = 0; + } + + if (DirContext->InjectionEntry.Buffer != NULL) { + + ExFreePoolWithTag( DirContext->InjectionEntry.Buffer, NC_TAG ); + DirContext->InjectionEntry.Buffer = NULL; + DirContext->InjectionEntry.CurrentOffset = 0; + } + + // + // Now that the cache is clear we can set up the injection entry. + // The injection entry is the user mapping itself. Thus it only needs + // to be injected if the directory being enumerated is the parent of + // the user mapping. + // + + if (UserMappingOverlap.Parent) { + + Status = NcEnumerateDirectorySetupInjection( DirContext, + FltObjects, + InstanceContext, + Offsets, + InformationClass ); + if (!NT_SUCCESS( Status )) { + + goto NcStreamHandleContextEnumSetupCleanup; + } + } + } + + // + // Now we know that the entry is setup. + // Mark it as in use. + // + + DirContext->InUse = TRUE; + Status = STATUS_SUCCESS; + +NcStreamHandleContextEnumSetupCleanup: + + if (!NT_SUCCESS( Status )) { + + if (!DirContext->InUse) { + + // + // We failed to set up the context for first use. + // We should free our buffer we allocated. + // + + if (DirContext->SearchString.Buffer != NULL) { + + ExFreePoolWithTag( DirContext->SearchString.Buffer, NC_TAG ); + DirContext->SearchString.Buffer = NULL; + DirContext->SearchString.Length = 0; + DirContext->SearchString.MaximumLength = 0; + } + } + } + + return Status; +} + +VOID +NcStreamHandleContextEnumClose ( + _In_ PNC_DIR_QRY_CONTEXT DirContext + ) +/*++ + +Routine Description: + + Tears down any remaining state associated with a directory enumeration. + +Arguments: + + DirContext - Pointer to the directory context. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + if (DirContext->Cache.Buffer != NULL) { + + ExFreePoolWithTag( DirContext->Cache.Buffer, + NC_DIR_QRY_CACHE_TAG ); + + DirContext->Cache.Buffer = NULL; + } + + if (DirContext->InjectionEntry.Buffer != NULL) { + + ExFreePoolWithTag( DirContext->InjectionEntry.Buffer, + NC_DIR_QRY_CACHE_TAG ); + + DirContext->InjectionEntry.Buffer = NULL; + } + + if (DirContext->SearchString.Buffer != NULL) { + + ExFreePoolWithTag( DirContext->SearchString.Buffer, + NC_DIR_QRY_SEARCH_STRING ); + + DirContext->SearchString.Buffer = NULL; + } +} + + diff --git a/filesys/miniFilter/NameChanger/ncdirnotify.c b/filesys/miniFilter/NameChanger/ncdirnotify.c new file mode 100644 index 00000000..480f647f --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncdirnotify.c @@ -0,0 +1,3526 @@ +/*++ + +Copyright (c) 2009 Microsoft Corporation + +Module Name: + + ncdirnotify.c + +Abstract: + + Contains routines to process user-initiated directory change notifications. + Depending on path, we may need to suppress the real mapping and its + children from generating notifications to the user, or inject changes from + the user mapping to the user. In some cases we can optimize this merely + by transforming the real mapping to user mapping (if we are watching an + ancestor of both paths.) + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +// +// The following structures define a context which is allocated per sub-request. +// We record the type of the subrequest, and any state that is subrequest +// specific. +// +typedef enum _NC_NOTIFY_REQUEST_TYPE { + NotifyUserRequest = 0, + NotifyShadowRequest, + NotifyMappingRequest +} NC_NOTIFY_REQUEST_TYPE; + +typedef struct _NC_NOTIFY_REQUEST_CONTEXT { + PNC_STREAM_HANDLE_CONTEXT UserHandleContext; + PFILE_OBJECT FileObjectToDereference; + NC_NOTIFY_REQUEST_TYPE RequestType; + PFLT_CALLBACK_DATA Request; +} NC_NOTIFY_REQUEST_CONTEXT, *PNC_NOTIFY_REQUEST_CONTEXT; + +NTSTATUS +NcAllocateNotifyRequestContext ( + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, + _In_ PFILE_OBJECT FileObject, + _In_ NC_NOTIFY_REQUEST_TYPE RequestType, + _In_ PFLT_CALLBACK_DATA Request, + _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext + ); + +VOID +NcFreeNotifyRequestContext ( + _In_ PNC_NOTIFY_REQUEST_CONTEXT NotifyRequestContext + ); + +FLT_POSTOP_CALLBACK_STATUS +NcPostNotifyDirectorySafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +VOID +NcPostNotifyDirectoryReal ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PVOID CompletionContext + ); + +NTSTATUS +NcDirNotifyTranslateBuffers ( + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN IgnoreCase, + _In_ PUNICODE_STRING UserRequestName, + _In_ PUNICODE_STRING OpenedName, + _In_reads_bytes_(InputBufferLength) PFILE_NOTIFY_INFORMATION InputSystemBuffer, + _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NOTIFY_INFORMATION OutputUserBuffer, + _In_ ULONG InputBufferLength, + _In_ ULONG OutputBufferLength, + _Out_ PULONG InputBufferConsumed, + _Out_ PULONG OutputBufferWritten, + _In_ BOOLEAN ReturnRealMappingPaths, + _In_ BOOLEAN ReturnInMappingOnly + ); + + + +NTSTATUS +NcBuildSubNotifyRequest ( + _In_ PFLT_CALLBACK_DATA PrimaryRequest, + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, + _In_ NC_NOTIFY_REQUEST_TYPE RequestType, + _Out_ PFLT_CALLBACK_DATA * SubRequest, + _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext + ); + +VOID +NcCleanupSubNotifyRequest ( + _In_ PFLT_CALLBACK_DATA SubRequest + ); + +NTSTATUS +NcGetDestinationNotifyBuffer ( + _Inout_ PFLT_CALLBACK_DATA Data, + _Outptr_result_bytebuffer_maybenull_(*BufferSize) PVOID * DestBuffer, + _Out_ PULONG BufferSize + ); + +_Pre_satisfies_(*LockHeld) +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(*HandleContext->Lock) +_When_(*LockHeld == FALSE, _Releases_lock_(_Global_critical_region_)) +_When_(*LockHeld == FALSE, _Releases_lock_(*HandleContext->Lock)) +VOID +NcNotifyAbort( + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, + _In_ NTSTATUS Status, + _Inout_ PBOOLEAN LockHeld + ); + +VOID +NcNotifyCancelCallback( + _In_ PFLT_CALLBACK_DATA Data + ); + +VOID +NcCloseHandleWorkerRoutine ( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID HandlePtr + ); + +VOID +NcReissueNotifyRequestWorkerRoutine ( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID RequestPtr + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcAllocateNotifyRequestContext) +#pragma alloc_text(PAGE, NcBuildSubNotifyRequest) +#pragma alloc_text(PAGE, NcCleanupSubNotifyRequest) +#pragma alloc_text(PAGE, NcCloseHandleWorkerRoutine) +#pragma alloc_text(PAGE, NcDirNotifyTranslateBuffers) +#pragma alloc_text(PAGE, NcFreeNotifyRequestContext) +#pragma alloc_text(PAGE, NcGetDestinationNotifyBuffer) +#pragma alloc_text(PAGE, NcNotifyAbort) +#pragma alloc_text(PAGE, NcNotifyCancelCallback) +#pragma alloc_text(PAGE, NcPreNotifyDirectory) +#pragma alloc_text(PAGE, NcPostNotifyDirectorySafe) +#pragma alloc_text(PAGE, NcPostNotifyDirectoryReal) +#pragma alloc_text(PAGE, NcReissueNotifyRequestWorkerRoutine) +#pragma alloc_text(PAGE, NcStreamHandleContextNotCleanup) +#pragma alloc_text(PAGE, NcStreamHandleContextNotCreate) +#pragma alloc_text(PAGE, NcStreamHandleContextNotClose) +#endif + +#define NcRemoveTrailingSlashIfPresent( US ) \ + FLT_ASSERT( (US)->Length > 0 ); \ + if ( (US)->Buffer[(US)->Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { \ + (US)->Length -= sizeof(WCHAR); \ + } + + +NTSTATUS +NcDirNotifyTranslateBuffers ( + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN IgnoreCase, + _In_ PUNICODE_STRING UserRequestName, + _In_ PUNICODE_STRING OpenedName, + _In_reads_bytes_(InputBufferLength) PFILE_NOTIFY_INFORMATION InputSystemBuffer, + _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NOTIFY_INFORMATION OutputUserBuffer, + _In_ ULONG InputBufferLength, + _In_ ULONG OutputBufferLength, + _Out_ PULONG InputBufferConsumed, + _Out_ PULONG OutputBufferWritten, + _In_ BOOLEAN ReturnRealMappingPaths, + _In_ BOOLEAN ReturnInMappingOnly + ) +/*++ + +Routine Description: + + This routine is used to transform buffers from a filesystem view to the + user view. Depending on flags we either replace references to the real + mapping with the user mapping, suppress all entries from the real mapping, + or only return results from inside the real mapping. + +Arguments: + + InstanceContext - Pointer to the context describing this instance of the + filter. + + IgnoreCase - TRUE if comparisons should be case insensitive, FALSE if + comparisons should be case sensitive. + + UserRequestName - The string for the path that the user opened and is + requesting notifications on. + + OpenedName - The string for the path that we are processing notifications + on. "Typically" the same as UserRequestName, but may be different if + we are merging notifications from the user's handle and combining + from the real mapping. In this case, OpenedName may refer to the + path to the real mapping. + + InputSystemBuffer - The buffer we are processing from. Note that this + routine assumes the buffer is not volatile (cannot be externally + modified.) For this reason, the buffer is expected to be system + buffered by the caller if it is not already. + + Note however that the contents of the buffer may have originated from + a user buffer (via a memcpy), so although the contents are non-volatile, + they are not to be trusted. + + OutputUserBuffer - The buffer we are returning munged results into. + This buffer is expected to have been probed, and this function will + catch and return any invalid buffer exceptions. + + InputBufferLength - Size, in bytes, of the input buffer. + + OutputBufferLength - Size, in bytes, of the output buffer. + + InputBufferConsumed - Pointer to a ULONG which will contain, on output, + the number of bytes processed from the input buffer. This may be + zero, the length of the input buffer, or any value in between. + This value is undefined on failure. + + OutputBufferWritten - Pointer to a ULONG which will contain, on output, + the number of bytes written into the output buffer. This may be + zero, the length of the output buffer, or any value in between. + This value is undefined on failure. + + ReturnRealMappingPaths - TRUE if this function should transform and + return any paths that are within the real mapping. If FALSE, we + omit these entries. + + ReturnInMappingOnly - TRUE if this function should transform and return + paths from within the real mapping only, and omit all other paths. + FALSE if it should return all paths. Only meaningful if + ReturnRealMappingPaths is TRUE. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + NC_PATH_OVERLAP RealOverlap; + + // + // Pointers into the above buffers used as we read entries from + // one buffer and write them to the other. We keep a pointer to + // the previous destination entry to allow us to terminate the + // list when done. + // + + PFILE_NOTIFY_INFORMATION SourceEntry = NULL; + PFILE_NOTIFY_INFORMATION DestEntry = NULL; + PFILE_NOTIFY_INFORMATION PrevDestEntry = NULL; + + // + // A full path to the path returned from the filesystem that we are + // currently examining; the Remainder of that path if it turns out + // to be in a mapping; the transformed name if one is required. + // + + UNICODE_STRING NameString = EMPTY_UNICODE_STRING; + UNICODE_STRING Remainder; + UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; + + // + // A pointer to one of the above buffers that we intend on returning to + // the application. Note that this may be NULL if an entry is being + // suppressed. + // + + PUNICODE_STRING ReturnName; + ULONG EntryLength; + + ULONG UlongResult; + PVOID PointerResult; + + PAGED_CODE(); + + SourceEntry = InputSystemBuffer; + DestEntry = OutputUserBuffer; + *InputBufferConsumed = 0; + *OutputBufferWritten = 0; + + // + // This routine assumes it will only be called if there is work to do. + // + + FLT_ASSERT( InputBufferLength && OutputBufferLength ); + + try { + + while (SourceEntry) { + + // + // The path returned is relative to the handle used to request + // the service. We now allocate and construct a full path name. + // Use safe math routines when consulting the buffer, since it is + // possible that the buffer contents were modified by malicious + // code before getting here. + // + + EntryLength = OpenedName->Length + + sizeof(WCHAR); + + if (EntryLength >= MAXUSHORT) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcDirNotifyTranslateBuffersCleanup; + } + + Status = RtlULongAdd( EntryLength, + SourceEntry->FileNameLength, + &EntryLength ); + + if (!NT_SUCCESS( Status ) || + (EntryLength >= MAXUSHORT)) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcDirNotifyTranslateBuffersCleanup; + } + + // + // Now check whether this entry walks off the end of the input buffer. + // + + Status = RtlULongAdd( *InputBufferConsumed, + FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName), + &UlongResult ); + + if (NT_SUCCESS( Status )) { + + Status = RtlULongAdd( UlongResult, + SourceEntry->FileNameLength, + &UlongResult ); + } + + if (!NT_SUCCESS( Status ) || + (UlongResult > InputBufferLength)) { + + // + // We have an entry that walks off the end of the buffer. + // Since the buffer that we get from the filesystem is not + // system buffered, we cannot guarantee that a caller is + // not corrupting it in an in-flight request. This + // condition should never occur without aforementioned + // corruption. + // + + FLT_ASSERT( FALSE ); + Status = STATUS_INVALID_USER_BUFFER; + goto NcDirNotifyTranslateBuffersCleanup; + } + + if (EntryLength > NameString.MaximumLength) { + + if (NameString.Buffer != NULL) { + NcFreeUnicodeString( &NameString ); + } + + NameString.Buffer = ExAllocatePoolWithTag( PagedPool, + EntryLength, + NC_TAG ); + + if (NameString.Buffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcDirNotifyTranslateBuffersCleanup; + } + + NameString.MaximumLength = (USHORT)EntryLength; + } + + _Analysis_assume_(NameString.Buffer != NULL); + RtlCopyMemory( NameString.Buffer, + OpenedName->Buffer, + OpenedName->Length ); + + NameString.Buffer[OpenedName->Length / sizeof(WCHAR)] = NC_SEPARATOR; + + RtlCopyMemory( Add2Ptr( NameString.Buffer, OpenedName->Length + sizeof(WCHAR) ), + SourceEntry->FileName, + SourceEntry->FileNameLength ); + + NameString.Length = (USHORT)EntryLength; + + // + // Now see if this name needs to be munged. + // + + NcComparePath( &NameString, + &InstanceContext->Mapping.RealMapping, + &Remainder, + IgnoreCase, + TRUE, + &RealOverlap ); + + if (RealOverlap.InMapping && ReturnRealMappingPaths) { + + // + // If the name is under the real mapping and we're returning + // paths under the real mapping, the query is a common + // ancestor of both real and user mappings. In this case, + // we need to translate real mapping paths to user mapping + // paths. + // + + Status = NcConstructPath( &InstanceContext->Mapping.UserMapping, + &Remainder, + TRUE, + &MungedName ); + + if (!NT_SUCCESS( Status )) { + goto NcDirNotifyTranslateBuffersCleanup; + } + + ReturnName = &MungedName; + + } else if (ReturnInMappingOnly || RealOverlap.InMapping) { + + // + // If the name is in the real mapping but the caller is asking + // for a tree which is not an ancestor of the user mapping, + // then this name should not be visible under this tree. If + // the name is an ancestor of the real mapping, but the caller + // only requires children, this name should not be visible + // under this tree either. + // + + ReturnName = NULL; + + } else { + + ReturnName = &NameString; + + } + + if (ReturnName != NULL) { + + ULONG EntryLengthExact; + + // + // Since this API returns paths under the one the query was + // issued on, the path we have now had better be longer than + // that path length. Even if we refer to the same object, + // our path includes a trailing slash. + // + + FLT_ASSERT( ReturnName->Length > UserRequestName->Length ); + _Analysis_assume_( ReturnName->Length > UserRequestName->Length ); + + // + // We take care not to copy the leading slash. If we have + // a trailing slash as well as a leading slash, truncate the + // string. This can occur when we're injecting an entry for + // the mapping. + // + + if (ReturnName->Length - UserRequestName->Length > sizeof(WCHAR) && + ReturnName->Buffer[ReturnName->Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { + + ReturnName->Length -= sizeof(WCHAR); + } + + EntryLengthExact = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName ); + EntryLengthExact += (ReturnName->Length - UserRequestName->Length - sizeof(WCHAR)); + + EntryLength = AlignToSize( EntryLengthExact, 8); + + // + // We've done all we can. Return now to let our caller deal + // with the remaining buffer. + // + + Status = RtlULongAdd( EntryLength, + *OutputBufferWritten, + &UlongResult ); + + if (!NT_SUCCESS( Status ) || + (UlongResult > OutputBufferLength)) { + + if (PrevDestEntry != NULL) { + PrevDestEntry->NextEntryOffset = 0; + } + + SourceEntry = NULL; + DestEntry = NULL; + break; + } + + // + // Copy the relative path name, taking care to exclude the + // initial slash. + // + + DestEntry->FileNameLength = ReturnName->Length - + UserRequestName->Length - + sizeof(WCHAR); + + RtlCopyMemory( DestEntry->FileName, + Add2Ptr( ReturnName->Buffer, UserRequestName->Length + sizeof(WCHAR)), + ReturnName->Length - UserRequestName->Length - sizeof(WCHAR)); + DestEntry->Action = SourceEntry->Action; + DestEntry->NextEntryOffset = EntryLength; + + // + // Advance the destination that we're writing new entries by + // however much we just consumed. + // + + PrevDestEntry = DestEntry; + *OutputBufferWritten += EntryLength; + DestEntry = Add2Ptr( DestEntry, EntryLength ); + + if (MungedName.Buffer != NULL) { + ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); + MungedName.Buffer = NULL; + MungedName.MaximumLength = MungedName.Length = 0; + } + } + + // + // Now calculate and advance the location we're reading and + // processing from. If we're have no more buffer left, we're + // done. + // + + // + // SourceEntry->NextEntryOffset is untrusted, since it may have been + // copied from a user-provided buffer. + // + + EntryLength = SourceEntry->NextEntryOffset; + + PointerResult = Add2Ptr( SourceEntry, EntryLength ); + + Status = RtlULongAdd( *InputBufferConsumed, + EntryLength, + InputBufferConsumed ); + + // + // There's a problem if one of the following happened: + // + // 1) We overflowed when accounting for for consumed input buffer + // 2) We wrapped when advancing SourceEntry + // 3) PointerResult is not within InputSystemBuffer + // + + if (!NT_SUCCESS( Status ) || + (PointerResult < (PVOID)SourceEntry) || + (PointerResult < Add2Ptr( InputSystemBuffer, sizeof(FILE_NOTIFY_INFORMATION) ))) { + + FLT_ASSERT( FALSE ); + + Status = STATUS_INVALID_USER_BUFFER; + goto NcDirNotifyTranslateBuffersCleanup; + } + + SourceEntry = (PFILE_NOTIFY_INFORMATION)PointerResult; + + if ((EntryLength == 0)) { + + if (PrevDestEntry != NULL) { + PrevDestEntry->NextEntryOffset = 0; + } + + *InputBufferConsumed = InputBufferLength; + SourceEntry = NULL; + } + + // + // If we've just advanced our next location beyond the end of the + // input buffer, or there isn't enough room in it for even a FILE_NOTIFY_INFORMATION + // structure, terminate the loop by setting SourceEntry to NULL so + // we can at least return the valid entries we have. + // + + FLT_ASSERT( *InputBufferConsumed <= InputBufferLength ); + + if ((SourceEntry != NULL) && + ((*InputBufferConsumed >= InputBufferLength) || + (*InputBufferConsumed + FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName ) > InputBufferLength))) { + + // + // Indicate to the caller that we consumed exactly the input buffer. + // Otherwise it may hold the possibly small remnant and come back + // in to this routine later with that remnant, causing us to overread + // the buffer. + // + + *InputBufferConsumed = InputBufferLength; + SourceEntry = NULL; + } + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + // + // If we're succeeding, our list should be terminated. + // + + FLT_ASSERT( PrevDestEntry == NULL || + !NT_SUCCESS( Status ) || + PrevDestEntry->NextEntryOffset == 0); + +NcDirNotifyTranslateBuffersCleanup: + + if (MungedName.Buffer != NULL) { + + ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); + MungedName.Buffer = NULL; + MungedName.MaximumLength = MungedName.Length = 0; + + } + + if (NameString.Buffer != NULL) { + + NcFreeUnicodeString( &NameString ); + } + + return Status; +} + +NTSTATUS +NcAllocateNotifyRequestContext ( + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, + _In_ PFILE_OBJECT FileObject, + _In_ NC_NOTIFY_REQUEST_TYPE RequestType, + _In_ PFLT_CALLBACK_DATA Request, + _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext + ) +/*++ + +Routine Description: + + This function allocates a new request context with the specified + properties. It references objects passed into it. + +Arguments: + + HandleContext - Pointer to the handle context attached to the + user's handle. + + FileObject - The file that this request is being sent to. This + will be different to the user's file object for + mapping requests. + + RequestType - Specifies which class of subrequest this context + is for. + + Request - Pointer to Fltmgr's notion of this request. + + NotifyRequestContext - On output, points to the newly allocated + context. On failure, this value is + undefined. + +Return Value: + + Status of the operation. + +--*/ +{ + PNC_NOTIFY_REQUEST_CONTEXT RequestContext; + + PAGED_CODE(); + + RequestContext = ExAllocatePoolWithTag( PagedPool, + sizeof(NC_NOTIFY_REQUEST_CONTEXT), + NC_TAG ); + + if (RequestContext == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + FltReferenceContext( HandleContext ); + RequestContext->UserHandleContext = HandleContext; + + if (ARGUMENT_PRESENT( FileObject )) { + + ObReferenceObject( FileObject ); + RequestContext->FileObjectToDereference = FileObject; + } + + RequestContext->RequestType = RequestType; + RequestContext->Request = Request; + + *NotifyRequestContext = RequestContext; + + return STATUS_SUCCESS; +} + +VOID +NcFreeNotifyRequestContext ( + _In_ PNC_NOTIFY_REQUEST_CONTEXT NotifyRequestContext + ) +/*++ + +Routine Description: + + This function frees a request context previously allocated with + NcAllocateNotifyRequestContext. Since it dereferences objects + referenced at allocate time, it must be called without locks + held. + +Arguments: + + NotifyRequestContext - The request context to tear down. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + FLT_ASSERT( ExIsResourceAcquiredSharedLite( NotifyRequestContext->UserHandleContext->Lock ) == 0 ); + + if (NotifyRequestContext->FileObjectToDereference) { + ObDereferenceObject( NotifyRequestContext->FileObjectToDereference ); + } + + FltReleaseContext( NotifyRequestContext->UserHandleContext ); + + ExFreePoolWithTag( NotifyRequestContext, NC_TAG ); + +} + +NTSTATUS +NcBuildSubNotifyRequest ( + _In_ PFLT_CALLBACK_DATA PrimaryRequest, + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, + _In_ NC_NOTIFY_REQUEST_TYPE RequestType, + _Out_ PFLT_CALLBACK_DATA * SubRequest, + _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext + ) +/*++ + +Routine Description: + + This routine is used to create and initialize a new subrequest for + directory change notifications. We need to create a new subrequest + when requests are being shadowed or when merging from multiple locations. + For change notifications unrelated to the mapping or spanning both + mapping locations, we can send the user's request to the filesystem and + not create any subrequests. + +Arguments: + + PrimaryRequest - Pointer to the user's request. We use this to propagate + settings into the sub request. + + Instance - Pointer to the instance of this filter. + + FileObject - Pointer to the file object for the subrequest. This may be + the same as the user's file object, or may be a file object to the + parent of the real mapping. + + HandleContext - Pointer to the handle context attached to the + user's handle. + + RequestType - Specifies which class of subrequest this context + is for. + + SubRequest - Pointer to a location to contain the new request created by + this routine. This value is undefined on failure. + + NotifyRequestContext - On output, points to the newly allocated + context. On failure, this value is undefined. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PVOID MyBuffer; + ULONG BufferLength = PrimaryRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length; + NTSTATUS Status; + PFLT_CALLBACK_DATA NewRequest; + + PAGED_CODE(); + + if (BufferLength > 0) { + + // + // Since this request isn't being sent to the filesystem and + // we'll complete it in arbitary context, we need to build a + // MDL now so we can copy to system address space rather than + // back to the originating usermode process. + // + + Status = FltLockUserBuffer( PrimaryRequest ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + // + // The #pragma is a notation to the static code analyzer to not worry + // that we're apparently leaking MyBuffer. It goes in to the subrequest + // we are building and will be cleaned up by NcCleanupSubNotifyRequest. + // That routine gets called on error in this routine, and from + // NcPostNotifyDirectoryReal, which is the completion routine that will + // be called once the subrequest we are building is finished executing. + // + +#pragma warning(suppress: 6014) + MyBuffer = ExAllocatePoolWithTag( PagedPool, + BufferLength, + NC_TAG ); + + if (MyBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + return Status; + } + + } else { + + MyBuffer = NULL; + } + + Status = FltAllocateCallbackData( Instance, + FileObject, + &NewRequest ); + + if (!NT_SUCCESS( Status )) { + + if (MyBuffer != NULL) { + ExFreePoolWithTag( MyBuffer, NC_TAG ); + } + + return Status; + } + + NewRequest->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; + NewRequest->Iopb->MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY; + NewRequest->Iopb->OperationFlags = PrimaryRequest->Iopb->OperationFlags; + NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length = BufferLength; + NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer = MyBuffer; + NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter = + PrimaryRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter; + NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare1 = 0; + NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare2 = 0; + NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress = NULL; + + if (BufferLength > 0) { + + NewRequest->Flags |= FLTFL_CALLBACK_DATA_SYSTEM_BUFFER; + } + + // + // Allocate the request context. We do this last because we + // cannot safely drop references if we fail after this point. + // + + Status = NcAllocateNotifyRequestContext( HandleContext, + FileObject, + RequestType, + NewRequest, + NotifyRequestContext ); + + if (!NT_SUCCESS( Status )) { + + NcCleanupSubNotifyRequest( NewRequest ); + return Status; + } + + *SubRequest = NewRequest; + return STATUS_SUCCESS; +} + +VOID +NcCleanupSubNotifyRequest ( + _In_ PFLT_CALLBACK_DATA SubRequest + ) +/*++ + +Routine Description: + + This routine is used to tear down a subrequest once we are done with it. + +Arguments: + + SubRequest - Pointer to the request to tear down. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + if (SubRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer != NULL) { + + ExFreePoolWithTag( SubRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer, + NC_TAG ); + } + + FltFreeCallbackData( SubRequest ); +} + +NTSTATUS +NcGetDestinationNotifyBuffer ( + _Inout_ PFLT_CALLBACK_DATA Data, + _Outptr_result_bytebuffer_maybenull_(*BufferSize) PVOID * DestBuffer, + _Out_ PULONG BufferSize + ) +/*++ + +Routine Description: + + This routine is used to obtain the virtual address that we should use + to access a particular request's buffer. Depending on the request, + this may be system buffered, system mapped but user exposed, or user + VA. + +Arguments: + + Data - The request whose VA we are trying to obtain/map. + + DestBuffer - Pointer to a pointer which will, on output, point to the + buffer associated with this request. On failure, this value is + undefined. + + BufferSize - Pointer to a location which will, on output, contain the + length of the user's buffer. On failure, this value is undefined. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PAGED_CODE(); + + *BufferSize = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length; + + *DestBuffer = NULL; + + // + // FltLockUserBuffer doesn't like being called with zero length buffers. + // If we have one of those, end now. + // + + if (Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length == 0) { + + Status = STATUS_SUCCESS; + goto NcGetDestinationNotifyBufferCleanup; + } + + // + // If we are in our post routine, we expect the buffer to have already + // been locked if FltGetNewSystemBufferAddress does not exist. If it + // does exist, we may have a MDL, or we may be obtaining the system + // buffer supplied by the filesystem. + // + // If we are issuing our own requests, we can also consume the user + // buffer directly, since it will always be system mapped (it's a pool + // allocation.) + // + + if (Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress) { + + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + goto NcGetDestinationNotifyBufferCleanup; + } + + *DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + if (*DestBuffer == NULL) { + + // + // If this fails, we have the pages locked in RAM but don't + // have VA to map them to. STATUS_NO_MEMORY is usually used to + // indicate VA exhaustion. + // + + Status = STATUS_NO_MEMORY; + goto NcGetDestinationNotifyBufferCleanup; + } + } else { + + if (NcGetNewSystemBufferAddress) { + *DestBuffer = NcGetNewSystemBufferAddress( Data ); + } else { + *DestBuffer = NULL; + } + + // + // If we have no MDL, and the filesystem hasn't allocated a system + // buffer, there are two possibilities: + // + // 1. This is a request we created ourselves and was already system + // buffered. + // + // 2. We are returning within the original thread context (ie., + // buffered results being returned to the caller instantly.) + // In this case, we can use the user's VA. + // + + if (*DestBuffer == NULL) { + + *DestBuffer = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer; + + if (NcGetNewSystemBufferAddress == NULL) { + + // + // On Win7 where we have FltGetNewSystemBufferAddress, we + // can grab the system buffer from the Irp. Pre-Win7 we + // must lock the user's buffer, thereby ending up in the + // above code block. If we roll our own, the buffers we + // use are in kernel space. The rule here is, no user VA + // can be sent down without being locked first. + // + + FLT_ASSERT( *DestBuffer > (PVOID)0x80000000 ); + + } else { + + try { + + if (Data->RequestorMode != KernelMode) { + ProbeForWrite( *DestBuffer, *BufferSize, sizeof( UCHAR )); + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + } + } + + } + +NcGetDestinationNotifyBufferCleanup: + + return Status; +} + + +FLT_PREOP_CALLBACK_STATUS +NcPreNotifyDirectory( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Routine is invoked when the user wants to modify a directory for + changes. The user can specify to monitor any directory, and can + opt to monitor an entire subtree. + + We can approach this problem in one of four ways: + + 1. If the directory being monitored has no relation to either + mapping, we can send it straight to the filesystem. + + 2. If the directory being monitored is an ancestor of the real + mapping but not the user mapping, we must suppress entries + from the mapping. + + 3. If the directory being monitored is an ancestor of both + mappings, we can change the contents of the buffer from real + to user to reflect the change. + + 4. If the directory being monitored is an ancestor of the user + mapping but not the real mapping, we must send down two + requests, one for the mapping parent and one for the user's + handle, and combine the results back to the user. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the handle context for + directory change notifications. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS ReturnValue; + NTSTATUS Status; + + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; + PNC_DIR_NOT_CONTEXT NotCtx = NULL; + + PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; + + NC_PATH_OVERLAP RealOverlap; + NC_PATH_OVERLAP UserOverlap; + + BOOLEAN WatchTree = BooleanFlagOn( Data->Iopb->OperationFlags, SL_WATCH_TREE ); + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + BOOLEAN UnlockContext = FALSE; + ULONG SizeWeReturn = 0; + PNC_NOTIFY_REQUEST_CONTEXT ShadowRequestContext = NULL; + PNC_NOTIFY_REQUEST_CONTEXT MappingParentRequestContext = NULL; + PFLT_CALLBACK_DATA NewShadowRequest = NULL; + PFLT_CALLBACK_DATA NewMappingParentRequest = NULL; + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the FileObject has been through cleanup, fail now. This check + // is to catch where a file was cleaned up, then a notification was + // called on it for the first time. We don't want to start + // initializing our structures now if we don't have to. + // + + if (FlagOn( FltObjects->FileObject->Flags, FO_CLEANUP_COMPLETE )) { + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_NOTIFY_CLEANUP; + SizeWeReturn = 0; + + goto NcPreNotifyDirectoryCleanup; + } + + // + // Get our instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + // + // Get the directory's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, + &FileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + Status = FltParseFileNameInformation( FileNameInformation ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + // + // See if the directory is parent of either mapping. + // + + NcComparePath( &FileNameInformation->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + NcComparePath( &FileNameInformation->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + // + // If we are watching within the mapping, we have no + // work to do. Strings returned are relative to the + // handle used to query for this operation. + // + + if (UserOverlap.InMapping || RealOverlap.InMapping) { + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreNotifyDirectoryCleanup; + } + + // + // If this object has no relation to either mapping, + // we have nothing to do. + // + + if (!UserOverlap.Ancestor && !RealOverlap.Ancestor) { + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreNotifyDirectoryCleanup; + } + + // + // If we are not monitoring recursively, and this is a + // non-parent ancestor, we have no work to do. + // + + if (!WatchTree && + (!UserOverlap.Parent && !RealOverlap.Parent)) { + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreNotifyDirectoryCleanup; + } + + // + // Attach our handle context. We use this to record + // state about which object we're monitoring, how we're + // monitoring it, and our outstanding subrequests. + // + + Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, + FltObjects->Instance, + FltObjects->FileObject, + &HandleContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + FLT_ASSERT( HandleContext != NULL ); + + NotCtx = &HandleContext->DirectoryNotificationContext; + + // + // Before looking at the context, we have to acquire the lock. + // + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + // + // If the user's handle has gone through cleanup, don't allow this + // request. We're not synchronized with the FO_CLEANUP_COMPLETE + // flag, but we are synchronized with the CleanupSeen flag, so we + // need to do this check now after acquiring the lock. + // + + if (NotCtx->CleanupSeen || + FlagOn( FltObjects->FileObject->Flags, FO_CLEANUP_COMPLETE )) { + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_NOTIFY_CLEANUP; + SizeWeReturn = 0; + + goto NcPreNotifyDirectoryCleanup; + } + + // + // Save away the path used on this handle, if we haven't already. + // This is done to avoid having to re-request it in post. + // + // Note that NameChanger will not allow renames of ancestor + // components, which are also the only paths it intercepts + // directory change notifications on. Accordingly, the path we + // capture here should not change for the lifetime of the + // notification. + // + + if (NotCtx->UserRequestName.Buffer == NULL) { + + NotCtx->UserRequestName.Buffer = ExAllocatePoolWithTag( PagedPool, + FileNameInformation->Name.Length, + NC_TAG ); + + if (NotCtx->UserRequestName.Buffer == NULL) { + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPreNotifyDirectoryCleanup; + + } + + RtlCopyMemory( NotCtx->UserRequestName.Buffer, + FileNameInformation->Name.Buffer, + FileNameInformation->Name.Length ); + + NotCtx->UserRequestName.MaximumLength = + NotCtx->UserRequestName.Length = + FileNameInformation->Name.Length; + + NcRemoveTrailingSlashIfPresent( &NotCtx->UserRequestName ); + + NotCtx->IgnoreCase = IgnoreCase; + + FltReferenceContext( InstanceContext ); + NotCtx->InstanceContext = InstanceContext; + NotCtx->CompletionFilter = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter; + NotCtx->OperationFlags = Data->Iopb->OperationFlags; + + } + + // + // If the user had previously cancelled a request on this handle, + // clear that now. We want to process all results from this + // point. + // + + NotCtx->CancelSeen = FALSE; + + // + // TODO: The notify package can record a number of Irps for the + // same directory (with the same settings for all.) To preserve + // this semantic, we need to save off whether we're watching the + // entire tree or not in our handle context, then keep a list + // of outstanding requests. These will be completed by the + // filesystem on a first in, first out basis. Or perhaps, we + // maintain the list and only send the filesystem a "representative" + // query, but maintain our own list and re-send to the filesystem + // if further user requests exist. + // + // Bah. + // + + // Check if this ever occurs in practice + FLT_ASSERT( NotCtx->UserRequest == NULL ); + if (NotCtx->UserRequest != NULL) { + + Status = STATUS_UNSUCCESSFUL; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + // + // If we previously saw the filesystem indicate that the buffer was + // not large enough to return all results, but we did not have a user + // request to complete to that effect, let the user know now. + // + + if (NotCtx->InsufficientBufferSeen) { + + NotCtx->InsufficientBufferSeen = FALSE; + Status = STATUS_NOTIFY_ENUM_DIR; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + // + // If we still have leftover changes from a previous call, + // return them now. Note that we do not want to set this + // request as being the user's request in this case. + // + + if (NotCtx->BufferToFree != NULL) { + + PVOID Buffer; + ULONG BufferSize; + + FLT_ASSERT( NotCtx->BufferLength != 0 ); + + Status = NcGetDestinationNotifyBuffer( Data, &Buffer, &BufferSize ); + + // + // We were unable to lock/map the user's buffer, or the buffer + // was invalid. + // + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + // + // If the user's buffer isn't big enough, tell them to rescan. + // Since we're telling them to rescan, tear down the buffer. + // + + if (BufferSize < NotCtx->BufferLength) { + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_NOTIFY_ENUM_DIR; + goto NcPreNotifyDirectoryCleanup; + + } else { + + try { + + RtlCopyMemory( Buffer, + NotCtx->BufferToFree, + NotCtx->BufferLength ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + goto NcPreNotifyDirectoryCleanup; + } + + SizeWeReturn = NotCtx->BufferLength; + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_SUCCESS; + + goto NcPreNotifyDirectoryCleanup; + } + } + + NotCtx->UserRequest = Data; + + // + // If we are watching the parent of the real mapping, + // or an ancestor of it including subtree, but not + // an ancestor of the user mapping, we'll need to "filter" + // out notifications that a caller should not see. + // + + if ((WatchTree && RealOverlap.Ancestor && !UserOverlap.Ancestor) || + (!WatchTree && RealOverlap.Parent && !UserOverlap.Parent)) { + + NotCtx->Mode = Filter; + + // + // TODO: Currently the lifetime of requests in filter mode is + // lock-step. If we get here when the Irp is already sent to + // the filesystem, that implies that we have multiple async + // notify irps sent to us, and we either support having + // multiple notify irps sent to the filesystem, or we queue + // them here. + // + + FLT_ASSERT( NotCtx->ShadowRequest == NULL ); + if (NotCtx->ShadowRequest == NULL) { + + Status = NcBuildSubNotifyRequest( Data, + FltObjects->Instance, + FltObjects->FileObject, + HandleContext, + NotifyShadowRequest, + &NewShadowRequest, + &ShadowRequestContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + } + + // + // To synchronize this, we set the request in the context under + // cover of the context lock. We then drop the lock to issue the + // IO. The rule here is that IO should only be sent by its + // creator or its completor. + // + + if (NewShadowRequest) { + + FLT_ASSERT( ShadowRequestContext ); + FLT_ASSERT( UnlockContext ); + + // + // Set up the subrequest. This is done before we set up a + // cancel callback to ensure that the cancel can do + // something meaningful if it's invoked. + // + + NotCtx->ShadowRequest = NewShadowRequest; + + // + // Set a cancel routine on the user's request (which we intend + // on hanging on to.) If the request is already cancelled, + // cancel our new request. + // + + Status = NcSetCancelCompletion( Data, NcNotifyCancelCallback ); + + if (!NT_SUCCESS( Status )) { + + NotCtx->ShadowRequest = NULL; + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + FLT_ASSERT( HandleContext != NULL ); + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + // + // As soon as we send this, Data/NotCtx->UserRequest is + // no longer guaranteed to be valid. Fltmgr will call + // our completion routine even on failure. Regardless + // of what happens here, we do not need to worry about + // cleaning things up. + // + + Status = FltPerformAsynchronousIo( NotCtx->ShadowRequest, + NcPostNotifyDirectoryReal, + ShadowRequestContext ); + + } + + // + // Don't free the request or its context. These belong to the + // request now. + // + + ShadowRequestContext = NULL; + NewShadowRequest = NULL; + + // + // We return pending here in all cases. Either the filesystem + // pended the request (and we should too), or the filesystem + // completed it inline, in which case our completion routine has + // already completed our 'pending' request, so we'd better pend it. + // + // If our request failed, Fltmgr will call our completion routine + // which will also propagate the failure to the user request and + // complete it. + // + + ReturnValue = FLT_PREOP_PENDING; + + } else + + // + // If we are watching the tree of a mutual ancestor, + // or just the directory of a mutal parent, or within + // the mapping itself, we'll need to transform names. + // + + if ((WatchTree && RealOverlap.Ancestor && UserOverlap.Ancestor) || + (RealOverlap.Parent && UserOverlap.Parent)) { + + // + // Filesystems may switch a directory change notify request + // to system buffered. On Win7, we can obtain the resulting + // system buffer from FltGetNewSystemBufferAddress. Prior + // to this we can't obtain it, so we must lock the user + // buffer now so the filesystem can obtain kernel VA and will + // not attempt to system buffer the request. + // + + if (NcGetNewSystemBufferAddress == NULL) { + + Status = FltLockUserBuffer( Data ); + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + if (MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ) == NULL) { + + Status = STATUS_NO_MEMORY; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + } + + // + // Allocate a context that describes this subrequest. + // + + Status = NcAllocateNotifyRequestContext( HandleContext, + FltObjects->FileObject, + NotifyUserRequest, + Data, + &ShadowRequestContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + *CompletionContext = ShadowRequestContext; + ShadowRequestContext = NULL; + + // + // We have to do something with this call, so tell + // fltmgr to call us back. + // + + ReturnValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + NotCtx->Mode = Munge; + + } else + + // + // If we are watching the tree of an ancestor of the + // user mapping or just the directory of the parent + // of the user mapping, which is not an ancestor of + // the real mapping, we need to merge two different + // notifications into one. + // + + if ((WatchTree && UserOverlap.Ancestor && !RealOverlap.Ancestor) || + (!WatchTree && UserOverlap.Parent && !RealOverlap.Parent)) { + + // + // If we already have a handle to the mapping parent, use it. + // If we don't have one, drop our lock and proceed to open it. + // + + if (NotCtx->RealParentHandle == NULL) { + + OBJECT_ATTRIBUTES MappingParentAttributes; + HANDLE MappingParentHandle = NULL; + PFILE_OBJECT MappingParentFileObject = NULL; + PWSTR MappingParentName; + IO_STATUS_BLOCK MappingParentStatusBlock; + PFLT_FILE_NAME_INFORMATION FileInfoInternalHandle = NULL; + PFLT_GENERIC_WORKITEM MappingParentWorkItem = NULL; + + // + // Since we haven't opened a handle yet, we assume no requests + // can be outstanding which could race with us. Even if the + // directory being used has been renamed so we're going into + // this path after being through one of the other paths, we + // should be in lock-step until we enter Merge mode, so no + // requests should exist. + // + + FLT_ASSERT( NotCtx->ShadowRequest == NULL && + NotCtx->MappingRequest == NULL ); + + FLT_ASSERT( UnlockContext ); + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + // + // Open the parent of the mapping. We'll need to return results + // from both the user's handle and our own. Note that we open + // the parent specifically so that a caller can monitor changes + // to the mapping itself. + // + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // InstanceOffsets + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction info + + if (!NT_SUCCESS( Status )) { + + // + // This filter enforces the existence of mapping parents + // at attach time, and will prevent them being deleted or + // renamed. + // + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + + } + + Status = NcGetFileNameInformation( NULL, + MappingParentFileObject, + FltObjects->Instance, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfoInternalHandle ); + if (!NT_SUCCESS( Status )) { + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + Status = FltParseFileNameInformation( FileInfoInternalHandle ); + if (!NT_SUCCESS( Status )) { + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + FltReleaseFileNameInformation( FileInfoInternalHandle ); + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + MappingParentName = ExAllocatePoolWithTag( PagedPool, + FileInfoInternalHandle->Name.Length, + NC_TAG ); + + if (MappingParentName == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + FltReleaseFileNameInformation( FileInfoInternalHandle ); + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + MappingParentWorkItem = FltAllocateGenericWorkItem(); + if (MappingParentWorkItem == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + FltReleaseFileNameInformation( FileInfoInternalHandle ); + ExFreePoolWithTag( MappingParentName, NC_TAG ); + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + // + // We could be racing with another thread attempting to open the + // mapping. If, after having reacquired the lock we still see + // no mapping handle, use ours. If another thread beat us to it, + // tear down ours and use theirs. + // + + if (NotCtx->RealParentHandle == NULL && + !NotCtx->CleanupSeen) { + + // + // Store our object into the context. + // + + FLT_ASSERT( NotCtx->RealParentFileObject == NULL && + NotCtx->RealParentCloseWorkItem == NULL && + NotCtx->MappingParentName.Buffer == NULL ); + + NotCtx->RealParentHandle = MappingParentHandle; + NotCtx->RealParentFileObject = MappingParentFileObject; + NotCtx->RealParentCloseWorkItem = MappingParentWorkItem; + + NotCtx->MappingParentName.Buffer = MappingParentName; + NotCtx->MappingParentName.MaximumLength = FileInfoInternalHandle->Name.Length; + + RtlCopyMemory( NotCtx->MappingParentName.Buffer, + FileInfoInternalHandle->Name.Buffer, + FileInfoInternalHandle->Name.Length ); + + NotCtx->MappingParentName.Length = FileInfoInternalHandle->Name.Length; + + NcRemoveTrailingSlashIfPresent( &NotCtx->MappingParentName ); + + FltReleaseFileNameInformation( FileInfoInternalHandle ); + + } else { + + // + // Another thread beat us to the punch, or our services are + // no longer required. Tear down our state and reload from + // theirs. + // + // Really, we can't race here (currently) because: + // + // 1. We do not support multiple outstanding requests on one + // handle (if attempted, we already failed.) + // + // 2. This must be the first request on this handle, or else + // our mapping parent handle already existed. + // + // 3. We have not yet set up cancellation on this request. + // + // The only way to be here is if a cleanup request has + // occurred and we're aborting. + // + + FLT_ASSERT( NotCtx->CleanupSeen ); + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + ExFreePoolWithTag( MappingParentName, NC_TAG ); + FltReleaseFileNameInformation( FileInfoInternalHandle ); + FltFreeGenericWorkItem( MappingParentWorkItem ); + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + // + // Another thread beat us to creating the file object, but + // it was subsequently torn down while we dropped our lock. + // This can happen if the user handle is closed, in which + // case we expect CleanupSeen to be set and we can safely + // leave. Return pending because the cleanup completed our + // request. + // + + if (NotCtx->RealParentHandle == NULL) { + + FLT_ASSERT( NotCtx->CleanupSeen ); + + ReturnValue = FLT_PREOP_PENDING; + goto NcPreNotifyDirectoryCleanup; + } + } + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + MappingParentName = NULL; + + // + // This relationship may have changed while we dropped the + // lock, but this can't happen in practice today: + // + // 1. We do not support multiple outstanding requests on one + // handle (if attempted, we already failed.) + // + // 2. This must be the first request on this handle, or else + // our mapping parent handle already existed. + // + // 3. We have not yet set up cancellation on this request. + // + // 4. If the user handle were closed, we failed out above. + // + // If these change and we need to revalidate state, we may + // need to clear cancel state, return insufficient buffer, + // or return buffered data here. + // + + FLT_ASSERT( NotCtx->UserRequest == Data ); + + FLT_ASSERT( NotCtx->ShadowRequest == NULL && + NotCtx->MappingRequest == NULL && + !NotCtx->CleanupSeen && + !NotCtx->CancelSeen && + !NotCtx->InsufficientBufferSeen && + NotCtx->BufferToFree == NULL && + NotCtx->BufferLength == 0 ); + + } + + FLT_ASSERT( UnlockContext ); + FLT_ASSERT( NotCtx->RealParentHandle != NULL && + NotCtx->RealParentFileObject != NULL && + NotCtx->RealParentCloseWorkItem != NULL ); + + NotCtx->Mode = Merge; + + // + // Now create our child requests. We need to watch for changes on + // both the user's handle and on the mapping parent handle. While + // this process is ongoing, we're trying to drop/reacquire the lock + // as few times as possible, so we set up both requests at once, + // then issue them together. + // + // If this is the second (or subsequent) time notifications are + // being requested, we may already have subrequests issued to the + // filesystem. In that case, we leave the existing requests in + // place. Any request not already in place we create ourselves + // now. + // + + if (NotCtx->ShadowRequest == NULL) { + + Status = NcBuildSubNotifyRequest( Data, + FltObjects->Instance, + FltObjects->FileObject, + HandleContext, + NotifyShadowRequest, + &NewShadowRequest, + &ShadowRequestContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + } + + if (NotCtx->MappingRequest == NULL) { + + Status = NcBuildSubNotifyRequest( Data, + FltObjects->Instance, + NotCtx->RealParentFileObject, + HandleContext, + NotifyMappingRequest, + &NewMappingParentRequest, + &MappingParentRequestContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + } + + // + // To synchronize this, we set the request in the context under cover + // of the context lock. We then drop the lock to issue the IO. + // The rule here is that IO should only be sent by its creator or its + // completor. + // + + if (NewShadowRequest || NewMappingParentRequest) { + + FLT_ASSERT( UnlockContext ); + + if (NewShadowRequest) { + NotCtx->ShadowRequest = NewShadowRequest; + } + + if (NewMappingParentRequest) { + NotCtx->MappingRequest = NewMappingParentRequest; + } + + // + // Set a cancel routine on the user's request (which we intend on + // hanging on to.) + // + + Status = NcSetCancelCompletion( Data, NcNotifyCancelCallback ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreNotifyDirectoryCleanup; + } + + FLT_ASSERT( HandleContext != NULL ); + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + // + // Note that as soon as we drop the lock, the user's request is + // no longer guaranteed to be valid. Any previously outstanding + // IO may complete and take it away from us. + // + + if (NewShadowRequest) { + + Status = FltPerformAsynchronousIo( NotCtx->ShadowRequest, + NcPostNotifyDirectoryReal, + ShadowRequestContext ); + + ShadowRequestContext = NULL; + NewShadowRequest = NULL; + + + // + // If we failed and have a second request, grab the lock + // again to enable our cleanup code to tear it down. + // + + if (!NT_SUCCESS( Status ) && NewMappingParentRequest) { + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + ReturnValue = FLT_PREOP_PENDING; + goto NcPreNotifyDirectoryCleanup; + + } + } + + if (NewMappingParentRequest) { + + Status = FltPerformAsynchronousIo( NotCtx->MappingRequest, + NcPostNotifyDirectoryReal, + MappingParentRequestContext ); + + MappingParentRequestContext = NULL; + NewMappingParentRequest = NULL; + } + } + + ReturnValue = FLT_PREOP_PENDING; + + } + + FLT_ASSERT( NotCtx->Mode != Uninitialized ); + + FLT_ASSERT( NotCtx->InstanceContext != NULL ); + + +NcPreNotifyDirectoryCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + // + // We need to write back results of query. + // + + Data->IoStatus.Status = Status; + + if (NT_SUCCESS( Status )) { + + //success + Data->IoStatus.Information = SizeWeReturn; + + } else { + + //failure + Data->IoStatus.Information = 0; + + } + + // + // Since we're completing the request, remove it from the notify + // context structure. Note that since we're completing it, it + // must still be valid and owned by us, so we're absolutely + // entitled (and required!) to remove it. + // + + if (HandleContext != NULL) { + + if (!UnlockContext) { + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + } + + if (NotCtx->UserRequest == Data) { + NotCtx->UserRequest = NULL; + } + } + } + + if (NewShadowRequest) { + + // + // We should only be cleaning this up if we hold the lock. + // + + FLT_ASSERT( UnlockContext ); + + // + // Either this request is still set in the context (and we'll + // take care of that), or it's been cancelled and hence + // removed already. In either case, we still need to tear it + // down. + // + + FLT_ASSERT( NotCtx->ShadowRequest == NULL || + NotCtx->ShadowRequest == NewShadowRequest ); + + NcCleanupSubNotifyRequest( NewShadowRequest ); + NotCtx->ShadowRequest = NewShadowRequest = NULL; + } + + if (NewMappingParentRequest) { + + // + // We should only be cleaning this up if we hold the lock. + // + + FLT_ASSERT( UnlockContext ); + + // + // Either this request is still set in the context (and we'll + // take care of that), or it's been cancelled and hence + // removed already. In either case, we still need to tear it + // down. + // + + FLT_ASSERT( NotCtx->MappingRequest == NULL || + NotCtx->MappingRequest == NewMappingParentRequest ); + + NcCleanupSubNotifyRequest( NewMappingParentRequest ); + NotCtx->MappingRequest = NewMappingParentRequest = NULL; + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (UnlockContext) { + + FLT_ASSERT( HandleContext != NULL ); + NcUnlockStreamHandleContext( HandleContext ); + } + + // + // Now that we've dropped the lock and can issue IO, tear down any + // request contexts from failed requests. + // + + if (ShadowRequestContext) { + NcFreeNotifyRequestContext( ShadowRequestContext ); + } + + if (MappingParentRequestContext) { + NcFreeNotifyRequestContext( MappingParentRequestContext ); + } + + if (HandleContext != NULL) { + + FltReleaseContext( HandleContext ); + } + + if (FileNameInformation != NULL) { + + FltReleaseFileNameInformation( FileNameInformation ); + } + + return ReturnValue; +} + + +VOID +NcPostNotifyDirectoryReal ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PVOID CompletionContext + ) +/*++ + +Routine Description: + + Routine is invoked after the filesystem has returned a change + notification, or if we are processing a cancellation for an + outstanding change notification. + + This routine is guaranteed to be called <= APC level. It is + still not really safe to issue IO from here, since that may + require us to be at passive level. We also have no guarantees + that TopLevelIrp == NULL at this point (and in many cases, we + wouldn't expect it to be.) Accordingly, this function should + not call back into the filesystem directly itself. + +Arguments: + + Data - Pointer to the request. This could be the user's request, + or it could be a subrequest we created for ourselves. + + CompletionContext - The context for the completion routine for + this operation. We set this to the handle context for + directory change notifications. + +Return Value: + + None. If we are completing a request, any errors will be + associated with that request. + +--*/ +{ + NTSTATUS Status; + + PFILE_NOTIFY_INFORMATION SourceBuffer = NULL; + PFILE_NOTIFY_INFORMATION DestBuffer; + + PNC_INSTANCE_CONTEXT InstanceContext; + PNC_NOTIFY_REQUEST_CONTEXT RequestContext = (PNC_NOTIFY_REQUEST_CONTEXT)CompletionContext; + PNC_STREAM_HANDLE_CONTEXT HandleContext = RequestContext->UserHandleContext; + PNC_DIR_NOT_CONTEXT NotCtx = NULL; + + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BufferSize; + ULONG InputConsumed; + ULONG SizeWeReturn; + + BOOLEAN UnlockContext = FALSE; + BOOLEAN CompleteUserRequest = TRUE; + BOOLEAN ReissuedRequest = FALSE; + + PAGED_CODE(); + + NotCtx = &HandleContext->DirectoryNotificationContext; + + // + // Before looking at the context, we have to acquire the lock. + // + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + // + // Perform debug-only sanity checking and set up our state so we + // can quickly tell which kind of request we're dealing with. + // + + FLT_ASSERT( NotCtx->Mode == Filter || NotCtx->Mode == Munge || NotCtx->Mode == Merge ); + FLT_ASSERT( RequestContext->Request == Data ); + + // + // If the user's handle has gone through cleanup, and we're completing + // it, make sure it fails correctly. Make sure we don't propagate any + // success or meaningful information beyond this point. + // + + if (NotCtx->CleanupSeen) { + if (NotCtx->UserRequest == NULL) { + CompleteUserRequest = FALSE; + } else { + Status = STATUS_NOTIFY_CLEANUP; + SizeWeReturn = 0; + } + goto NcPostNotifyDirectoryRealCleanup; + } + + // + // If the user has cancelled their request, make sure any completing + // subrequests are failed with STATUS_CANCELLED. This is primarily to + // ensure that we don't reissue requests post-cancel (which will never + // be cancelled.) + // + + if (NotCtx->CancelSeen) { + + Data->IoStatus.Status = STATUS_CANCELLED; + } + + // + // Flow any failures back to the user's request, if we have one. + // + + if (!NT_SUCCESS( Data->IoStatus.Status ) || + Data->IoStatus.Status == STATUS_NOTIFY_CLEANUP || + Data->IoStatus.Status == STATUS_NOTIFY_ENUM_DIR ) { + + Status = Data->IoStatus.Status; + SizeWeReturn = 0; + + if (NotCtx->UserRequest == NULL) { + + CompleteUserRequest = FALSE; + + if (Data->IoStatus.Status == STATUS_NOTIFY_ENUM_DIR) { + + // + // If the filesystem is trying to tell the app to rescan, + // but we don't have the app's request right now, make a + // note to tell the app that it must rescan as soon as it + // asks us again. + // + + NotCtx->InsufficientBufferSeen = TRUE; + } + } + goto NcPostNotifyDirectoryRealCleanup; + } + + FLT_ASSERT( NotCtx->Mode != Merge || + (NotCtx->RealParentFileObject != NULL && + NotCtx->UserRequestName.Buffer != NULL )); + + FLT_ASSERT( Data == NotCtx->UserRequest || + Data == NotCtx->ShadowRequest || + Data == NotCtx->MappingRequest ); + + InstanceContext = NotCtx->InstanceContext; + + Status = NcGetDestinationNotifyBuffer( Data, &DestBuffer, &BufferSize ); + if (!NT_SUCCESS( Status )) { + + // + // This can fail due no pages/VA to lock the user's buffer, or a + // 'user buffer' that points to kernel space. By the time we get + // here, we expect that our pages are either already locked or we + // do not need to lock them; and we expect that requests we + // generate internally are based on pool and should not require VA + // to map, nor be usermode pointers into kernel space. Really, the + // only time this should fail is if we have no VA to map the user's + // request. + // + // If this assumption is wrong, we need to be smarter about whether + // we want to complete the user's request in this path or not. + // + + FLT_ASSERT( Data == NotCtx->UserRequest ); + goto NcPostNotifyDirectoryRealCleanup; + } + + FLT_ASSERT( SizeActuallyReturned <= BufferSize ); + + // + // If we have a zero-length buffer, the filesystem should not have + // returned STATUS_SUCCESS. We still try to tolerate this + // condition regardless. + // + + FLT_ASSERT( BufferSize > 0 ); + + // + // If the filesystem returned something, go ahead and try to munge it. + // + + if (SizeActuallyReturned > (ULONG)FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName )) { + + // + // Allocate a new buffer and copy the contents. Note that this is + // particularly important with this call, since it's not always + // system buffered; the contents are free to change underneath us. + // This allocation protects us against that, but we still must be + // paranoid touching the buffer, since we cannot trust that it has + // any integrity at this point. + // + + SourceBuffer = ExAllocatePoolWithTag( PagedPool, + SizeActuallyReturned, + NC_TAG ); + + if (SourceBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostNotifyDirectoryRealCleanup; + } + + try { + + // + // The #pragma is a notation to the static code analyzer that the + // buffer sizes involved have been computed correctly so no buffer + // overrun is possible here. + // +#pragma warning(suppress:6385) + RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + goto NcPostNotifyDirectoryRealCleanup; + } + + // + // Now that we have a copy of the filesystem data, we need to + // transform it and return it to the user. So now we point our + // destination buffers at the original request if we own it, if it + // wasn't already. + // + // When merging from the mapping and non-mapping paths, we may have + // completed the user's request from one path and then receive + // notification on the second. In this case, we buffer the + // notification so the caller can receieve it again next call. We + // do not expect to need more than one buffer. If this overflows, + // just tell the caller their buffer was too small. + // + + if (RequestContext->RequestType == NotifyShadowRequest || + RequestContext->RequestType == NotifyMappingRequest) { + + if (NotCtx->UserRequest != NULL) { + + Status = NcGetDestinationNotifyBuffer( NotCtx->UserRequest, + &DestBuffer, + &BufferSize ); + + if (!NT_SUCCESS( Status )) { + goto NcPostNotifyDirectoryRealCleanup; + } + + } else { + + FLT_ASSERT( NotCtx->Mode == Merge ); + + // + // We have at most two outstanding child requests, and if + // one completes we'll complete the master request, + // buffering with the second. We therefore only need one + // buffer - most of the time. + // + // There is a case where one buffer is insufficient: if the + // user issues a cancel while both child requests are + // completing, we may have a cancelled user request and two + // incoming pieces of data. In this case, it's safe to + // throw one away since the act of cancelling implies the + // user doesn't expect to see everything. However in that + // case we should have failed out above, and should never + // get here. + // + + FLT_ASSERT( NotCtx->BufferToFree == NULL ); + if (NotCtx->BufferToFree != NULL) { + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + } + + // + // There is no user request, don't complete it. + // + + CompleteUserRequest = FALSE; + + if (BufferSize > 0) { + + // + // Save away the output to our buffer. Note that we + // process the data now because we won't know which call + // generated the data later. + // + + NotCtx->BufferToFree = ExAllocatePoolWithTag( PagedPool, + BufferSize, + NC_TAG ); + + if (NotCtx->BufferToFree == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostNotifyDirectoryRealCleanup; + } + + } + + DestBuffer = NotCtx->BufferToFree; + FLT_ASSERT( NotCtx->BufferLength == 0 ); + } + } + + // + // If we have a zero length buffer, return to the user that something + // changed and do not attempt to return anything more. + // + + if (BufferSize > 0) { + + Status = NcDirNotifyTranslateBuffers( InstanceContext, + NotCtx->IgnoreCase, + &NotCtx->UserRequestName, + (RequestContext->RequestType == NotifyMappingRequest)? + &NotCtx->MappingParentName: + &NotCtx->UserRequestName, + SourceBuffer, + DestBuffer, + SizeActuallyReturned, + BufferSize, + &InputConsumed, + &SizeWeReturn, + (BOOLEAN)(NotCtx->Mode == Munge || NotCtx->Mode == Merge), + (BOOLEAN)(RequestContext->RequestType == NotifyMappingRequest) ); + + } else { + + FLT_ASSERT( NT_SUCCESS( Status )); + } + + + if (!NT_SUCCESS( Status )) { + + // + // If we failed, don't leave the garbage buffer to be picked + // up again. Tear it down now. + // + + if (DestBuffer == NotCtx->BufferToFree) { + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + } + goto NcPostNotifyDirectoryRealCleanup; + } + + // + // We couldn't fit the translated information into the caller's + // buffer. For directory change notifications, there is a special + // status to use in this situation, which tells the caller to + // rescan from scratch. It's a heavy hammer, but might teach the + // caller to use a bigger buffer. + // + // Unfortunately this is lost in translation between NT & Win32, + // so most applications end up guessing. + // + + if ((BufferSize == 0) || + (InputConsumed < SizeActuallyReturned)) { + + if (!CompleteUserRequest) { + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + NotCtx->InsufficientBufferSeen = TRUE; + } + + SizeWeReturn = 0; + Status = STATUS_NOTIFY_ENUM_DIR; + goto NcPostNotifyDirectoryRealCleanup; + + } else { + + Status = STATUS_SUCCESS; + + if (DestBuffer == NotCtx->BufferToFree) { + NotCtx->BufferLength = SizeWeReturn; + } + } + + // + // Our worst nightmare has come true. The filesystem completed the + // request but we filtered out all the contents. We certainly + // don't want to tell the caller nothing whatsoever happened, so we + // try to reissue this request. + // + + if (SizeWeReturn == 0) { + + PMDL OldMdl = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress; + PFLT_GENERIC_WORKITEM WorkItem; + + FLT_ASSERT( (NotCtx->Mode == Filter || NotCtx->Mode == Merge ) && + (RequestContext->RequestType == NotifyShadowRequest || RequestContext->RequestType == NotifyMappingRequest ) ); + + if (DestBuffer == NotCtx->BufferToFree) { + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + } + + Status = NcGetDestinationNotifyBuffer( Data, + &DestBuffer, + &BufferSize ); + if (!NT_SUCCESS( Status )) { + goto NcPostNotifyDirectoryRealCleanup; + } + + WorkItem = FltAllocateGenericWorkItem(); + if (WorkItem == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostNotifyDirectoryRealCleanup; + } + + FLT_ASSERT( RequestContext->Request == Data ); + + // + // Clear and reinitialize the request using the Mdl and buffer + // that it was previously using. Note that we know it will be + // system buffered if it has a buffer, because the only + // requests that can get here are requests we created. + // + + FltReuseCallbackData( Data ); + + Data->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; + Data->Iopb->MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY; + Data->Iopb->OperationFlags = NotCtx->OperationFlags; + Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length = + BufferSize; + Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer = + DestBuffer; + Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter = + NotCtx->CompletionFilter; + Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare1 = 0; + Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare2 = 0; + Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress = + OldMdl; + + if (BufferSize > 0) { + + Data->Flags |= FLTFL_CALLBACK_DATA_SYSTEM_BUFFER; + } + + // + // Post issuing new IO to passive level, where we are sure TopLevelIrp + // is actually NULL. + // + + Status = FltQueueGenericWorkItem( WorkItem, + NcGlobalData.FilterHandle, + NcReissueNotifyRequestWorkerRoutine, + DelayedWorkQueue, + RequestContext ); + + if (NT_SUCCESS( Status )) { + ReissuedRequest = TRUE; + CompleteUserRequest = FALSE; + } else { + goto NcPostNotifyDirectoryRealCleanup; + } + + } + + // + // If we successfully parsed and returned data either to the user's + // call or saved it to our buffer, we will not reissue the + // associated call. If the user calls us again, we'll repeat any + // completed call(s). Doing this allows the filter to delegate + // buffering of operations to the filesystem, except for the above + // case (both sides of a merge complete in parallel before the user + // calls again.) + // + + + } else { + + // + // If the filesystem returned something less than one single + // entry, return nothing. + // + + SizeWeReturn = 0; + if (NotCtx->UserRequest == NULL) { + CompleteUserRequest = FALSE; + } + } + +NcPostNotifyDirectoryRealCleanup: + + FLT_ASSERT( CompleteUserRequest || RequestContext->RequestType != NotifyUserRequest ); + + // + // We've just processed something from one of the child Irps. + // Tear it down, NULL it out. We'll rebuild it if we're asked + // for another change notification. + // + + if (!ReissuedRequest) { + + if (RequestContext->RequestType == NotifyShadowRequest || + RequestContext->RequestType == NotifyMappingRequest ) { + + if (Data == NotCtx->ShadowRequest) { + NotCtx->ShadowRequest = NULL; + } + + if (Data == NotCtx->MappingRequest) { + NotCtx->MappingRequest = NULL; + } + + // + // This routine will free the FLT_CALLBACK_DATA. + // + + NcCleanupSubNotifyRequest( Data ); + } + } + + // + // We need to write back results of query. + // + + if (CompleteUserRequest) { + + FLT_ASSERT( UnlockContext ); + + FLT_ASSERT( NotCtx->UserRequest != NULL ); + FLT_ASSERT( RequestContext->RequestType != NotifyUserRequest || + Data == NotCtx->UserRequest ); + + NotCtx->UserRequest->IoStatus.Status = Status; + + if (NT_SUCCESS( Status )) { + + //success + NotCtx->UserRequest->IoStatus.Information = SizeWeReturn; + + } else { + + //failure + NotCtx->UserRequest->IoStatus.Information = 0; + } + + if (RequestContext->RequestType != NotifyUserRequest) { + + PFLT_CALLBACK_DATA RequestToComplete = NotCtx->UserRequest; + NTSTATUS CancelStatus; + + // + // Clear the cancel routine. If cancel has already been + // invoked, don't complete now (leave the cancel routine to + // complete the request, and give the user nothing.) If + // cancel has not yet been invoked, after we clear the + // routine it cannot be invoked, so we are free to complete + // the request ourselves unconditionally. + // + + CancelStatus = FltClearCancelCompletion( RequestToComplete ); + + if (CancelStatus != STATUS_CANCELLED) { + + NotCtx->UserRequest = NULL; + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + FltCompletePendedPreOperation( RequestToComplete, + FLT_PREOP_COMPLETE, + NULL ); + } + + } else { + + NotCtx->UserRequest = NULL; + } + } + + if (SourceBuffer != NULL) { + + ExFreePoolWithTag( SourceBuffer, NC_TAG ); + } + + if (UnlockContext) { + + FLT_ASSERT( HandleContext != NULL ); + NcUnlockStreamHandleContext( HandleContext ); + } + + // + // Now that we've dropped the lock, drop our references. + // + + if (!ReissuedRequest) { + NcFreeNotifyRequestContext( RequestContext ); + } + +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostNotifyDirectorySafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + Routine is invoked when a directory change notification request + completes. The request must be the user's request. This + function is called when <= APC_LEVEL. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. For directory change notifications, this is a pointer + to our handle context. + + Flags - Flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + + PAGED_CODE(); + + NcPostNotifyDirectoryReal( Data, CompletionContext ); + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( FltObjects ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_POSTOP_CALLBACK_STATUS +NcPostNotifyDirectory ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + Routine is invoked when a directory change notification request + completes. The request must be the user's request. This routine + may be called at DISPATCH_LEVEL and must be nonpaged. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. For directory change notifications, this is a pointer + to our handle context. + + Flags - Flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + + FLT_POSTOP_CALLBACK_STATUS Status; + BOOLEAN Success; + + if (FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) { + + // + // We can only be here on a user initiated operation that we sent + // to the filesystem. + // + // TODO: Really what we want to do here is return + // STATUS_NOTIFY_ENUM_DIR to the caller, but we no longer own the + // request. Should we bite the bullet and shadow all munge requests + // so that this can work? + // + + NcFreeNotifyRequestContext( CompletionContext ); + + Status = FLT_POSTOP_FINISHED_PROCESSING; + + } else { + + // + // For simplicity, and to avoid needing everything to be nonpaged, + // we perform completion at APC_LEVEL or below. + // + + Success = FltDoCompletionProcessingWhenSafe( Data, + FltObjects, + CompletionContext, + Flags, + NcPostNotifyDirectorySafe, + &Status ); + + if (!Success) { + + // + // It would be nice to know _why_ this fails, but since we + // don't, neither will anybody else. + // + + Data->IoStatus.Status = STATUS_UNSUCCESSFUL; + Data->IoStatus.Information = 0; + Status = FLT_POSTOP_FINISHED_PROCESSING; + } + } + + return Status; +} + +VOID +NcReissueNotifyRequestWorkerRoutine ( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID RequestPtr + ) +/*++ + +Routine Description: + + This function reissues an subrequest that is part of an + oustanding directory change notification request. If it is + unable to reissue the request, this function is responsible + for all cleanup of the request. + +Arguments: + + WorkItem - Pointer to the workitem which triggered the call to this + function. + + Filter - Pointer to our filter object. + + RequestPtr - Our context, specifically the request we wish to reissue. + +Return Value: + + None. + +--*/ +{ + PNC_NOTIFY_REQUEST_CONTEXT RequestContext = (PNC_NOTIFY_REQUEST_CONTEXT)RequestPtr; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Filter ); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + FLT_ASSERT( RequestContext->RequestType == NotifyShadowRequest || + RequestContext->RequestType == NotifyMappingRequest ); + + // + // Since we're being called asynchronously, it's possible + // that this request has already been cancelled, or that + // cleanup has already occurred. In the event of a cancel, + // this request should be marked as canceled and the + // filesystem will complete it immediately. In the event + // of cleanup, we are sending the request to a cleaned up + // handle and the filesystem will complete it immediately. + // + // On failure, Fltmgr will call our completion routine, + // so we don't need to clean up here. + // + + (VOID) FltPerformAsynchronousIo( RequestContext->Request, + NcPostNotifyDirectoryReal, + RequestContext ); + + FltFreeGenericWorkItem( WorkItem ); +} + +VOID +NcCloseHandleWorkerRoutine ( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID HandlePtr + ) +/*++ + +Routine Description: + + This function performs a handle close operation. Since handle + close operations must occur at passive, we queue a workitem to + close the handle when we cannot do so inline. This function + actually performs the close. + +Arguments: + + WorkItem - Pointer to the workitem which triggered the call to this + function. + + Filter - Pointer to our filter object. + + HandlePtr - Our context, specifically the handle we wish to close. + +Return Value: + + None. + +--*/ +{ + HANDLE Handle = (HANDLE)HandlePtr; + NTSTATUS DummyStatus; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Filter ); + + DummyStatus = FltClose( Handle ); + FLT_ASSERT( NT_SUCCESS( DummyStatus )); + + FltFreeGenericWorkItem( WorkItem ); +} + +_Pre_satisfies_(*LockHeld) +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(*HandleContext->Lock) +_When_(*LockHeld == FALSE, _Releases_lock_(_Global_critical_region_)) +_When_(*LockHeld == FALSE, _Releases_lock_(*HandleContext->Lock)) +VOID +NcNotifyAbort ( + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, + _In_ NTSTATUS Status, + _Inout_ PBOOLEAN LockHeld + ) +/*++ + +Routine Description: + + This function aborts a change notify operation. It is shared code + called from both cancel and handle cleanup. It assumes that it is + called with the context lock already held, and may drop the lock + if it needs to complete or cancel requests. + +Arguments: + + HandleContext - The context describing the request we wish to abort. + + Status - Status code to abort with. This should be STATUS_CANCELLED + for cancel requests or STATUS_NOTIFY_CLEANUP for cleanup requests. + + LockHeld - Pointer to a boolean value. On input, this should contain + a value indicating whether the handle context lock is acquired + (and it should be.) On output, this will be set to TRUE if the + lock is acquired or FALSE if not. + +Return Value: + + None. + +--*/ +{ + PNC_DIR_NOT_CONTEXT NotCtx = &HandleContext->DirectoryNotificationContext; + + FLT_ASSERT( *LockHeld ); + + PAGED_CODE(); + + // + // If we are hanging on to the user's request, complete it now. The status + // code for this is very specific. Note that in the Munge case the request + // is owned by the filesystem, so we leave the filesystem to deal with this + // when it gets the cleanup request. + // + // Cancel any outstanding requests we have issued (if any.) + // + + if ((NotCtx->Mode == Filter) || + (NotCtx->Mode == Merge)) { + + PFLT_CALLBACK_DATA RequestToComplete = NotCtx->UserRequest; + PFLT_CALLBACK_DATA RequestToCancel1 = NotCtx->ShadowRequest; + PFLT_CALLBACK_DATA RequestToCancel2 = NotCtx->MappingRequest; + HANDLE HandleToClose = NotCtx->RealParentHandle; + PFILE_OBJECT FileObjectToDereference = NotCtx->RealParentFileObject; + PFLT_GENERIC_WORKITEM WorkItem = NotCtx->RealParentCloseWorkItem; + + NotCtx->ShadowRequest = NULL; + NotCtx->MappingRequest = NULL; + NotCtx->RealParentHandle = NULL; + NotCtx->RealParentFileObject = NULL; + NotCtx->RealParentCloseWorkItem = NULL; + + // + // Attempt to clear the cancel callback on the user's request. If + // this fails, it may indicate that we are cancelling the irp ourselves, + // or may indicate that we're in cleanup and racing with a cancel. + // When racing with cancel, cancel always wins. + // + + if (RequestToComplete) { + NTSTATUS CancelStatus; + + CancelStatus = FltClearCancelCompletion( RequestToComplete ); + + if (CancelStatus == STATUS_CANCELLED && Status != STATUS_CANCELLED) { + RequestToComplete = NULL; + } else { + NotCtx->UserRequest = NULL; + } + } + + NcUnlockStreamHandleContext( HandleContext ); + *LockHeld = FALSE; + + if (RequestToComplete) { + RequestToComplete->IoStatus.Status = Status; + RequestToComplete->IoStatus.Information = 0; + + FltCompletePendedPreOperation( RequestToComplete, FLT_PREOP_COMPLETE, NULL ); + } + + + // + // Cancel any outstanding IOs, ignoring status. + // + // It is possible that we can't cancel because: + // + // 1. The request hasn't reached the filesystem so there's no cancel + // routine. This is not a problem, since the below call will + // mark the request as cancelled so it will complete immediately. + // + // 2. The request is already being completed. It's no big deal if + // we let it complete, just so long as it finishes soon. Note + // that we've already set CleanupSeen/CancelSeen by this point, + // so unless we're cancelling and get a new user request, the + // outcome will be the same as if our cancel succeeded. + // + + if (RequestToCancel1) { + + (VOID)FltCancelIo( RequestToCancel1 ); + } + + if (RequestToCancel2) { + + (VOID)FltCancelIo( RequestToCancel2 ); + } + + if (HandleToClose) { + + NTSTATUS WorkItemStatus; + + FLT_ASSERT( WorkItem != NULL ); + + WorkItemStatus = FltQueueGenericWorkItem( WorkItem, + NcGlobalData.FilterHandle, + NcCloseHandleWorkerRoutine, + DelayedWorkQueue, + HandleToClose ); + + // + // Queuing the work item will fail if the filter is unloading. All + // we're doing is a handle close, which will get taken care of when + // the process we're in eventually goes away. However we've still + // got an allocated work item to take care of. + // + + if (!NT_SUCCESS( WorkItemStatus )) { + + FltFreeGenericWorkItem( WorkItem ); + } + } + + if (FileObjectToDereference) { + ObDereferenceObject( FileObjectToDereference ); + } + } +} + +VOID +NcNotifyCancelCallback( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This function is called to cancel a change notify operation. + +Arguments: + + Data - The request to cancel. This should be caller-initiated (or we + shouldn't have set a cancel routine on it.) + +Return Value: + + None. + +--*/ +{ + PNC_STREAM_HANDLE_CONTEXT HandleContext; + PNC_DIR_NOT_CONTEXT NotCtx; + NTSTATUS Status; + BOOLEAN UnlockContext; + + PAGED_CODE(); + + // + // Try to figure out the context associated with this request. + // It would be really nice if we had a FltObjects structure for + // this. Are filters really supposed to do this? + // + + Status = FltGetStreamHandleContext( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &HandleContext ); + + FLT_ASSERT( NT_SUCCESS( Status )); + if (!NT_SUCCESS( Status )) { + return; + } + + FLT_ASSERT( HandleContext != NULL ); + + NotCtx = &HandleContext->DirectoryNotificationContext; + + // + // Before looking at the context, we have to acquire the lock. + // + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + // + // We should only ever be called on the user's request. + // It follows that this is a Merge or Filter operation. + // + + FLT_ASSERT( Data == NotCtx->UserRequest ); + FLT_ASSERT( NotCtx->Mode == Filter || NotCtx->Mode == Merge ); + + // + // Indicate to completing requests that we are cancelling. + // While we are cancelling, if child requests are racing + // with us and completing legitimately, we force them to + // drain out. This flag will be cleared next time the + // user asks for a change notification on this handle. + // + + NotCtx->CancelSeen = TRUE; + + // + // If the user is cancelling, it follows that they cannot + // expect a complete picture of data. Tear down any held + // over buffer that we may happen to have, since it may be + // large and we're not optimizing for recurring work after + // cancel. + // + // We cannot tear down any file names at this point, since + // we're racing with IO completion which may depend on + // those names remaining intact. + // + + if (NotCtx->BufferToFree != NULL) { + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + } + + // + // This call may drop our locks. If it does, we are no + // longer synchronized against incoming requests and + // must not tear anything down any further. + // + + NcNotifyAbort( HandleContext, STATUS_CANCELLED, &UnlockContext ); + + if (UnlockContext) { + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + } + + FltReleaseContext( HandleContext ); +} + +NTSTATUS +NcStreamHandleContextNotCreate( + _Out_ PNC_DIR_NOT_CONTEXT Context + ) +/*++ + +Routine Description: + + This function is called to initialize the directory change notification + portion of a stream handle context. + +Arguments: + + Context - Pointer to the directory change notification portion of the + stream handle context. + +Return Value: + + Returns the status of the operation (currently only STATUS_SUCCESS.) + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + Context->Mode = Uninitialized; + Context->CleanupSeen = FALSE; + Context->InsufficientBufferSeen = FALSE; + Context->UserRequestName.Buffer = NULL; + Context->UserRequestName.Length = Context->UserRequestName.MaximumLength = 0; + + Context->MappingParentName.Buffer = NULL; + Context->MappingParentName.Length = Context->MappingParentName.MaximumLength = 0; + + Context->UserRequest = NULL; + Context->ShadowRequest = NULL; + Context->MappingRequest = NULL; + + Context->InstanceContext = NULL; + Context->RealParentHandle = NULL; + Context->RealParentFileObject = NULL; + + Context->BufferToFree = NULL; + Context->BufferLength = 0; + + return Status; +} + +VOID +NcStreamHandleContextNotCleanup( + _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext + ) +/*++ + +Routine Description: + + This function is called to process IRP_MJ_CLEANUP for a directory change + notification request. Directory change notifications are cancelled + with a special status code at handle cleanup time. We take this + opportunity to tear down anything we reasonably can, since a file may + be in limbo between cleanup and close for a long time. + +Arguments: + + HandleContext - Pointer to the stream handle context. + +Return Value: + + None. + +--*/ +{ + PNC_DIR_NOT_CONTEXT NotCtx = &HandleContext->DirectoryNotificationContext; + BOOLEAN UnlockContext; + + PAGED_CODE(); + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + // + // Set a flag to indicate that outstanding requests should complete immediately + // without propagating status to the user or buffering results. This also + // prevents new work starting after this point. + // + + FLT_ASSERT( !NotCtx->CleanupSeen ); + NotCtx->CleanupSeen = TRUE; + + // + // Complete our requests. Note that this routine may drop our lock. + // + + NcNotifyAbort( HandleContext, STATUS_NOTIFY_CLEANUP, &UnlockContext ); + + // + // Lock the structure again if necessary after performing any IO so we can + // continue teardown. Note that the user's request should not come back + // at this point in the case of cleanup. + // + + if (!UnlockContext) { + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + } + + // + // Once a handle is closed, it should stay closed. + // + + FLT_ASSERT( NotCtx->CleanupSeen ); + + FLT_ASSERT( NotCtx->UserRequest == NULL || + NotCtx->Mode == Munge); + + // + // Although requests may remain, if we've been through cleanup our post + // routine will ensure they can't do squat. + // + + if (NotCtx->UserRequestName.Buffer != NULL) { + NcFreeUnicodeString( &NotCtx->UserRequestName ); + } + + if (NotCtx->MappingParentName.Buffer != NULL) { + NcFreeUnicodeString( &NotCtx->MappingParentName ); + } + + if (NotCtx->BufferToFree != NULL) { + + ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG ); + NotCtx->BufferToFree = NULL; + NotCtx->BufferLength = 0; + } + + if (NotCtx->InstanceContext != NULL) { + FltReleaseContext( NotCtx->InstanceContext ); + NotCtx->InstanceContext = NULL; + } + + NotCtx->Mode = Uninitialized; + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; +} + +VOID +NcStreamHandleContextNotClose( + _In_ PNC_DIR_NOT_CONTEXT Context + ) +/*++ + +Routine Description: + + This function is called to tear down the directory change notification + portion of a stream handle context. + +Arguments: + + Context - Pointer to the directory change notification portion of the + stream handle context. + +Return Value: + + None. + +--*/ +{ + + PAGED_CODE(); + + // + // TODO: Determine when/how this is called. What IRQL are we really at + // here? Are we top level? What should we do if we're not? + // + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + FLT_ASSERT( !KeAreAllApcsDisabled() ); + + + // + // We need to be protected against any new work arriving on this handle. + // Typically we expect cleanup to have already been seen by this point, + // which will have torn down most state for this handle. This is not + // necessarily true when the filter is being detached. Since we can't + // tear down a context with active references, we expect that mapping/ + // shadow requests are currently NULL, and should not come back - either + // because CleanupSeen is set, or FltMgr is draining us. + // + + FLT_ASSERT( Context->MappingRequest == NULL && + Context->ShadowRequest == NULL ); + + // + // Firstly, close the handles. These can recurse back into the + // filesystem and generate notifications themselves, or cancel + // outstanding notifications, etc. Note that we do not hold the lock + // around this operation. + // + + if (Context->RealParentHandle != NULL) { + + FltClose( Context->RealParentHandle ); + + } + + // + // In theory, we should no longer have outstanding notifications at + // this point. The user has closed their handle (completing any + // shadow request) and we have closed ours (completing any mapping + // request.) Now we are safe to teardown, and we don't require a + // lock to do so. + // + + if (Context->RealParentFileObject != NULL) { + + ObDereferenceObject( Context->RealParentFileObject ); + } + + Context->RealParentFileObject = NULL; + Context->RealParentHandle = NULL; + + if (Context->RealParentCloseWorkItem != NULL) { + FltFreeGenericWorkItem( Context->RealParentCloseWorkItem ); + Context->RealParentCloseWorkItem = NULL; + } + + if (Context->UserRequestName.Buffer != NULL) { + NcFreeUnicodeString( &Context->UserRequestName ); + } + + if (Context->MappingParentName.Buffer != NULL) { + NcFreeUnicodeString( &Context->MappingParentName ); + } + + if (Context->BufferToFree != NULL) { + + ExFreePoolWithTag( Context->BufferToFree, NC_TAG ); + Context->BufferToFree = NULL; + Context->BufferLength = 0; + } + + Context->Mode = Uninitialized; + + if (Context->InstanceContext != NULL) { + FltReleaseContext( Context->InstanceContext ); + Context->InstanceContext = NULL; + } +} + + + diff --git a/filesys/miniFilter/NameChanger/ncfileinfo.c b/filesys/miniFilter/NameChanger/ncfileinfo.c new file mode 100644 index 00000000..ea5708e6 --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncfileinfo.c @@ -0,0 +1,1971 @@ +/*++ + +Copyright (c) 2008 - 2009 Microsoft Corporation + +Module Name: + + ncfileinfo.c + +Abstract: + + Contains routines to process user-initiated query file and set file + information requests. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcPreQueryAlternateName) +#pragma alloc_text(PAGE, NcPostQueryHardLinks) +#pragma alloc_text(PAGE, NcPostQueryName) +#pragma alloc_text(PAGE, NcPreSetDisposition) +#pragma alloc_text(PAGE, NcPreSetLinkInformation) +#pragma alloc_text(PAGE, NcPreSetShortName) +#pragma alloc_text(PAGE, NcPreRename) +#endif + +FLT_POSTOP_CALLBACK_STATUS +NcPostQueryName ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called when the user wants to request a name for a + previously opened handle. Since we munged the name to be to the real + mapping in pre-create, we must munge it back to the user visible view + in response to name requests, even by opened name. + + Note that this function processes three information classes: + + FileNameInformation + FileNormalizedNameInformation + FileAllInformation + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the handle context for + directory change notifications. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + NC_PATH_OVERLAP Overlap; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + FILE_INFORMATION_CLASS InfoClass = Data->Iopb->Parameters.QueryFileInformation.FileInformationClass; + PVOID UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer; + ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length; + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG LengthNeeded = 0; + ULONG UserStructureSize = 0; + ULONG RequiredNameSize = 0; + ULONG NameLengthAvailable = 0; + PFILE_NAME_INFORMATION NameInfo; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + UNICODE_STRING Remainder; + UNICODE_STRING RemainderCopy = EMPTY_UNICODE_STRING; + UNICODE_STRING ReturnedName; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the operation failed already, we have no processing to do unless the + // failure is a buffer overflow. + // + + if (!NT_SUCCESS( Data->IoStatus.Status ) && + (Data->IoStatus.Status != STATUS_BUFFER_OVERFLOW)) { + + Status = Data->IoStatus.Status; + goto NcPostQueryNameInformationCleanup; + } + + // + // Get our instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostQueryNameInformationCleanup; + } + + // + // Find the name in the user buffer. + // + + if (InfoClass == FileAllInformation) { + + NameInfo = & ((PFILE_ALL_INFORMATION) UserBuffer)->NameInformation; + + } else { + + FLT_ASSERT( InfoClass == FileNameInformation || + InfoClass == FileNormalizedNameInformation ); + + NameInfo = UserBuffer; + } + + // + // If the name is too long for a UNICODE_STRING, we can't process it. + // This should never really happen, since UNICODE_STRINGs are used + // all across the NT IO model. + // + + if (NameInfo->FileNameLength >= MAXUSHORT) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcPostQueryNameInformationCleanup; + } + + // + // Now that we have an instance context and NameInfo buffer, see if the file + // system failed with a buffer overflow. + // + + if (Data->IoStatus.Status == STATUS_BUFFER_OVERFLOW) { + + // + // We need to bias the FileNameLength field by the difference between + // the real and user mapping lengths if the user mapping is longer. This + // is so that if the caller re-issues the name query we won't fail with + // a buffer overflow in the filter even if the file system succeeded. + // + + if (InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length > + InstanceContext->Mapping.RealMapping.LongNamePath.VolumelessName.Length) { + + NameInfo->FileNameLength += InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length - + InstanceContext->Mapping.RealMapping.LongNamePath.VolumelessName.Length; + } + + Status = Data->IoStatus.Status; + LengthNeeded = SizeActuallyReturned; + + goto NcPostQueryNameInformationCleanup; + } + + ReturnedName.Buffer = NameInfo->FileName; + ReturnedName.MaximumLength = + ReturnedName.Length = (USHORT)NameInfo->FileNameLength; + + // + // Check if the name being returned is within the real mapping. + // If not, we have no translation to perform. + // + + NcComparePath( &ReturnedName, + &InstanceContext->Mapping.RealMapping, + &Remainder, + IgnoreCase, + FALSE, + &Overlap ); + + if (!Overlap.InMapping && !Overlap.Match) { + + Status = Data->IoStatus.Status; + LengthNeeded = SizeActuallyReturned; + + goto NcPostQueryNameInformationCleanup; + } + + // + // Make sure that the user buffer is long enough. + // + + UserStructureSize = FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ); + + if (InfoClass == FileAllInformation) { + + UserStructureSize += FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation ); + } + + RequiredNameSize = InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length; + + // + // Add back the trailing portion of the name. Note that Remainder + // is only defined if InMapping is TRUE. + // + + if (Overlap.InMapping && !Overlap.Match) { + + RequiredNameSize += Remainder.Length + sizeof(WCHAR); + } + + LengthNeeded = UserStructureSize + RequiredNameSize; + + // + // Whether the user has provided enough buffer or not, the + // FILE_NAME_INFORMATION.FileNameLength field has to contain the total length + // of the name we want to return. + // + + NameInfo->FileNameLength = RequiredNameSize; + + // + // If the user's buffer is not big enough to handle the name we need to return, + // we will copy in as much as we can and return STATUS_BUFFER_OVERFLOW. The + // user expects that the needed name length will be reported in the + // FILE_NAME_INFORMATION.FileNameLength field. + // + + if (UserBufferLength < LengthNeeded) { + + NameLengthAvailable = UserBufferLength - UserStructureSize; + + // + // Truncate the LengthNeeded value since it will be returned in the + // IoStatus block to tell I/O Manager how much to copy back to the + // user's buffer. + // + + LengthNeeded = UserBufferLength; + + Status = STATUS_BUFFER_OVERFLOW; + + // + // We have enough space. Let's assume we'll succeed to copy the name. + // + + } else { + + NameLengthAvailable = NameInfo->FileNameLength; + + Status = STATUS_SUCCESS; + } + + if (Overlap.InMapping && !Overlap.Match) { + + // + // Copy the remainder of the returned name from the user. This is + // done so that we can rewrite the user's buffer. Note that if we + // are not a match, we expect some remainder. + // + + FLT_ASSERT( Remainder.Length > 0 ); + + RemainderCopy.Buffer = ExAllocatePoolWithTag( PagedPool, + Remainder.Length, + NC_TAG ); + + if (RemainderCopy.Buffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostQueryNameInformationCleanup; + } + + RtlCopyMemory( RemainderCopy.Buffer, + Remainder.Buffer, + Remainder.Length ); + + RemainderCopy.MaximumLength = + RemainderCopy.Length = Remainder.Length; + } + + // + // Firstly, copy back the name to our mapping. + // + + RtlCopyMemory( &NameInfo->FileName, + InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Buffer, + min(InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length, + NameLengthAvailable) ); + + if (NameLengthAvailable > InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length) { + + NameLengthAvailable -= InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length; + + } else { + + NameLengthAvailable = 0; + } + + // + // If the object being queried is within the mapping, copy back the + // remainder of that name. + // + + if ((NameLengthAvailable > 0) && + Overlap.InMapping && !Overlap.Match) { + + NameInfo->FileName[InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length / sizeof(WCHAR)] = '\\'; + + RtlCopyMemory( Add2Ptr( &NameInfo->FileName, + InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length + sizeof(WCHAR) ), + RemainderCopy.Buffer, + min(RemainderCopy.Length, NameLengthAvailable) ); + } + + // + // We have finished the query, complete operation. + // + +NcPostQueryNameInformationCleanup: + + Data->IoStatus.Status = Status; + + // + // Note that STATUS_BUFFER_OVERFLOW is not a success code, but for name queries + // it indicates that the caller needs to allocate a bigger buffer. The needed + // size for the name is stored in the FILE_NAME_INFORMATION.FileNameLength field. + // Therefore the IoStatus.Information field must not be 0 for a buffer overflow, + // it must contain the size of the data that was actually copied. + // + + if (NT_SUCCESS( Status ) || + (Status == STATUS_BUFFER_OVERFLOW)) { + + Data->IoStatus.Information = LengthNeeded; + + } else { + + Data->IoStatus.Information = 0; + } + + if (RemainderCopy.Buffer != NULL) { + + ExFreePoolWithTag( RemainderCopy.Buffer, NC_TAG ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreQueryAlternateName ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is called when the user wants to find the alternate name + for a previously opened handle. An alternate name means the short + half of a long/short name pair. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the handle context for + directory change notifications. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS ReturnValue; + NC_PATH_OVERLAP Overlap; + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + PVOID UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer; + ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length; + ULONG LengthNeeded = 0; + PFILE_NAME_INFORMATION NameInfo; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + PUNICODE_STRING FinalComponentToReturn; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // Get our instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreQueryAlternateNameInformationCleanup; + } + + // + // Get the file's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreQueryAlternateNameInformationCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreQueryAlternateNameInformationCleanup; + } + + // + // We only need to handle the case where a shortname is being + // generated on the mapping itself. These names are final + // component path only, so any files within the mapping will + // still be correct even if we don't munge them. + // + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &Overlap ); + + if (!Overlap.Match) { + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &Overlap ); + + FLT_ASSERT( !Overlap.Match ); + + if (!Overlap.Match) { + + // + // This file is not the mapping, so we can just let this + // request go down normally. + // + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreQueryAlternateNameInformationCleanup; + } + } + + // + // Return the short name. + // + // Note that this behavior differs from the filesystem in two respects: + // + // 1. We are not attempting to detect (and fail for) an open-by-ID + // handle. Since these are link agnostic, returning data is + // meaningless. + // + // 2. We may support having multiple alternate names for multiple + // links on the file, if the mapping was created as a file then + // a hardlink was created with a mapping name. This file thus + // contains two shortnames, which NTFS does not support. In + // theory APIs should be clean to this (and a future filesystem + // may wish to support it.) + // + + FinalComponentToReturn = &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName; + + // + // Make sure that the user buffer is long enough. + // + + LengthNeeded = FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ); + + LengthNeeded += FinalComponentToReturn->Length; + + if (UserBufferLength < LengthNeeded) { + + Status = STATUS_BUFFER_OVERFLOW; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreQueryAlternateNameInformationCleanup; + } + + NameInfo = UserBuffer; + + // + // Copy back the name to our mapping. + // + + RtlCopyMemory( &NameInfo->FileName, + FinalComponentToReturn->Buffer, + FinalComponentToReturn->Length ); + + NameInfo->FileNameLength = FinalComponentToReturn->Length; + + // + // We have finished the query, complete operation. + // + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_COMPLETE; + +NcPreQueryAlternateNameInformationCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + Data->IoStatus.Status = Status; + + // + // Note that STATUS_BUFFER_OVERFLOW is not a success code, and + // will result in zero bytes being copied back to the caller. + // + + if (NT_SUCCESS( Status )) { + + Data->IoStatus.Information = LengthNeeded; + } else { + + Data->IoStatus.Information = 0; + } + } + + if (FileInfo != NULL) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + return ReturnValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +NcPostQueryHardLinks ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called when the user wants to enumerate all hard links + for a previously opened handle. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the handle context for + directory change notifications. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + // + // Pointer to the buffer returned to us, which we will also return to + // our caller; length of the buffer; size of the buffer filled in + // by the filesystem; size of the buffer that we filled in + // + + PFILE_LINKS_INFORMATION UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer; + ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length; + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BytesWritten = 0; + + // + // Length of the current entry that we're processing; a UNICODE_STRING + // for the final component of the name that we're processing; and + // a flag indicating whether this entry is being modified + // + + ULONG EntrySize; + UNICODE_STRING EntryName; + BOOLEAN MungeEntry; + + // + // Copy of the buffer returned from the filesystem, our iterators as + // we process this buffer, and a pointer to the previous destination + // entry (if one exists) so we can zero the offset to next entry + // field on completion + // + + PFILE_LINKS_INFORMATION OriginalBuffer = NULL; + PFILE_LINK_ENTRY_INFORMATION SourceEntry; + PFILE_LINK_ENTRY_INFORMATION DestEntry; + PFILE_LINK_ENTRY_INFORMATION PrevDestEntry = NULL; + + + // + // Variables that we use to obtain IDs to the mapping parents. + // + + OBJECT_ATTRIBUTES MappingParentAttributes; + HANDLE MappingParentHandle = NULL; + PFILE_OBJECT MappingParentFileObject = NULL; + IO_STATUS_BLOCK MappingParentStatusBlock; + + // + // File IDs for the mapping parents + // + + LONGLONG RealMappingParentId; + LONGLONG UserMappingParentId; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the buffer was invalid, or if the call failed, leave now. Note + // that this may be STATUS_BUFFER_OVERFLOW and the number of bytes the + // caller needs will be reported inaccurately. To handle this we'd + // really need to issue our own call to get the full buffer, then + // transform it to find the "correct" length the user will need. + // + // Rather than do this, we return the caller a value for bytes required + // which may not be accurate. When they call us again, we will + // have data to transform, and can then fail the call again specifying + // a new value for bytes required that is accurate. + // + + if (SizeActuallyReturned <= (ULONG)FIELD_OFFSET( FILE_LINKS_INFORMATION, Entry ) || + !NT_SUCCESS( Data->IoStatus.Status )) { + + BytesWritten = SizeActuallyReturned; + + Status = Data->IoStatus.Status; + goto NcPostQueryHardLinkInformationCleanup; + } + + // + // Get our instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostQueryHardLinkInformationCleanup; + } + + // + // Open the mapping parents and query IDs. + // + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + Data->Iopb->TargetInstance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + Data->Iopb->TargetFileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostQueryHardLinkInformationCleanup; + } + + Status = FltQueryInformationFile( Data->Iopb->TargetInstance, + MappingParentFileObject, + &RealMappingParentId, + sizeof(RealMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostQueryHardLinkInformationCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.UserMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + Data->Iopb->TargetInstance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + Data->Iopb->TargetFileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostQueryHardLinkInformationCleanup; + } + + Status = FltQueryInformationFile( Data->Iopb->TargetInstance, + MappingParentFileObject, + &UserMappingParentId, + sizeof(UserMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostQueryHardLinkInformationCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + // + // Take a copy of the results of the call from the filesystem. + // + + OriginalBuffer = ExAllocatePoolWithTag( PagedPool, + SizeActuallyReturned, + NC_TAG ); + + if (OriginalBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostQueryHardLinkInformationCleanup; + } + + RtlCopyMemory( OriginalBuffer, UserBuffer, SizeActuallyReturned ); + + // + // Set up our iterators to walk through the returned links. + // + + DestEntry = &UserBuffer->Entry; + + if (UserBuffer->EntriesReturned >= 1) { + SourceEntry = &OriginalBuffer->Entry; + } else { + SourceEntry = NULL; + } + UserBuffer->EntriesReturned = 0; + BytesWritten = FIELD_OFFSET( FILE_LINKS_INFORMATION, Entry ); + UserBuffer->BytesNeeded = BytesWritten; + + while( SourceEntry ) { + + // + // Assume we don't need to munge the link. If the parent IDs, + // final component lengths and final component strings correspond + // to the real mapping, we will need to transform it. + // + + MungeEntry = FALSE; + + if (SourceEntry->ParentFileId == RealMappingParentId) { + + if (SourceEntry->FileNameLength * sizeof(WCHAR) >= MAXUSHORT) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcPostQueryHardLinkInformationCleanup; + } + + EntryName.Buffer = SourceEntry->FileName; + EntryName.Length = EntryName.MaximumLength = (USHORT)SourceEntry->FileNameLength * sizeof(WCHAR); + + if (EntryName.Length == InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName.Length && + RtlCompareUnicodeString( &EntryName, &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName, IgnoreCase ) == 0) { + + MungeEntry = TRUE; + } + + // + // TODO: Preserve shortness in output + // + + if (EntryName.Length == InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName.Length && + RtlCompareUnicodeString( &EntryName, &InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName, IgnoreCase ) == 0) { + + MungeEntry = TRUE; + } + } + + // + // Calculate the length of the entry that we want to write. + // + + if (MungeEntry) { + + EntrySize = FIELD_OFFSET( FILE_LINK_ENTRY_INFORMATION, FileName ) + + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length; + + EntrySize = AlignToSize( EntrySize, 8 ); + + } else { + + EntrySize = FIELD_OFFSET( FILE_LINK_ENTRY_INFORMATION, FileName ) + + SourceEntry->FileNameLength * sizeof(WCHAR); + + EntrySize = AlignToSize( EntrySize, 8 ); + } + + // + // Record how much space the caller would need to return all entries. + // + + UserBuffer->BytesNeeded += EntrySize; + + // + // If we have space, copy this entry into the user's buffer and + // advance our destination iterator. + // + + if (BytesWritten + EntrySize <= UserBufferLength) { + + if (MungeEntry) { + + DestEntry->NextEntryOffset = EntrySize; + DestEntry->ParentFileId = UserMappingParentId; + DestEntry->FileNameLength = InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length / sizeof(WCHAR); + RtlCopyMemory( DestEntry->FileName, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Buffer, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length ); + } else { + + RtlCopyMemory( DestEntry, SourceEntry, EntrySize ); + } + PrevDestEntry = DestEntry; + DestEntry = Add2Ptr( DestEntry, EntrySize ); + UserBuffer->EntriesReturned++; + BytesWritten += EntrySize; + } + + // + // If we still have links that we have not yet consumed, move + // to those. + // + + if (SourceEntry->NextEntryOffset != 0) { + SourceEntry = Add2Ptr( SourceEntry, SourceEntry->NextEntryOffset ); + } else { + SourceEntry = NULL; + } + } + + // + // If we already copied one or more links, make sure our list is + // correctly terminated. + // + + if (PrevDestEntry != NULL) { + PrevDestEntry->NextEntryOffset = 0; + } + + // + // If we copied all results, return STATUS_SUCCESS. If we saw entries + // that we did not copy, return STATUS_BUFFER_OVERFLOW. + // + + if (BytesWritten == UserBuffer->BytesNeeded) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_BUFFER_OVERFLOW; + } + +NcPostQueryHardLinkInformationCleanup: + + Data->IoStatus.Status = Status; + + if (NT_SUCCESS( Status ) || Status == STATUS_BUFFER_OVERFLOW) { + + Data->IoStatus.Information = BytesWritten; + } else { + + Data->IoStatus.Information = 0; + } + + if (MappingParentHandle != NULL) { + + FltClose( MappingParentHandle ); + MappingParentHandle = NULL; + } + + if (MappingParentFileObject != NULL) { + + ObDereferenceObject( MappingParentFileObject ); + MappingParentFileObject = NULL; + } + + if (OriginalBuffer != NULL) { + + ExFreePoolWithTag( OriginalBuffer, NC_TAG ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreSetShortName ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is called when the user wants to change the short name + for a previously opened handle. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the handle context for + directory change notifications. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS ReturnValue; + NTSTATUS Status; + PFILE_NAME_INFORMATION NameInfo = + Data->Iopb->Parameters.SetFileInformation.InfoBuffer; + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NC_PATH_OVERLAP RealOverlap; + NC_PATH_OVERLAP UserOverlap; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + UNREFERENCED_PARAMETER( CompletionContext ); + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetShortNameCleanup; + } + + // + // Let's skip doing any of this work for file systems that we know don't + // have short names. + // + + if ((InstanceContext->VolumeFilesystemType == FLT_FSTYPE_EXFAT) || + (InstanceContext->VolumeFilesystemType == FLT_FSTYPE_REFS)) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetShortNameCleanup; + } + + // + // Get the file's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetShortNameCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetShortNameCleanup; + } + + // + // Calculate Overlap and Remainder + // + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + // + // Currently the file names that we use are read only, so changing a + // shortname on the mapping or any of its ancestors cannot be + // supported. + // + // TODO: Should we support this? + // + + if (RealOverlap.Match || + UserOverlap.Match || + RealOverlap.Ancestor || + UserOverlap.Ancestor || + NameInfo->FileNameLength > MAXUSHORT) { + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetShortNameCleanup; + } + + // + // If the user is attempting to set a name which is used by the real + // file, we let the request go to the file system (which will fail + // it.) For names used by the user mapping, we need to detect and + // fail those. + // + + if (UserOverlap.Peer) { + + UNICODE_STRING TargetComponent; + + TargetComponent.Buffer = NameInfo->FileName; + TargetComponent.Length = TargetComponent.MaximumLength = (USHORT)NameInfo->FileNameLength; + + if( RtlCompareUnicodeString( &TargetComponent, + &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName, + IgnoreCase) == 0 || + RtlCompareUnicodeString( &TargetComponent, + &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName, + IgnoreCase) == 0 ) { + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetShortNameCleanup; + } + + } + + // + // If the user is not setting a short name on our mapping, an ancestor + // of our mapping or targetting our mapping, let the request go to the + // file system. + // + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + +NcPreSetShortNameCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + Data->IoStatus.Status = Status; + } + + if (FileInfo != NULL) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + return ReturnValue; +} + + +FLT_PREOP_CALLBACK_STATUS +NcPreSetDisposition ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Fltmgr callback which manages setting the delete disposition on a file. + We must disallow setting the delete disposition on an ancestor of either + mapping because otherwise we would have to maintain the mapping's + short/long name pairings. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS ReturnValue; + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + PNC_MAPPING Mapping; + NC_PATH_OVERLAP RealOverlap; + NC_PATH_OVERLAP UserOverlap; + PFILE_DISPOSITION_INFORMATION Disposition; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( CompletionContext ); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // See if they are setting the delete disposition to false. + // If they are we can passthrough. We don't care if people + // want to mark the mapping as "don't delete". + // + + Disposition = Data->Iopb->Parameters.SetFileInformation.InfoBuffer; + if (Disposition->DeleteFile == FALSE) { + + Status = STATUS_SUCCESS; + goto NcPreSetDispositionCleanup; + } + + // + // The user is trying to delete a file. + // We have to make sure that the file is not an ancestor of either mapping. + // + + // + // Get the file's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo); + + if (!NT_SUCCESS( Status )) { + + goto NcPreSetDispositionCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcPreSetDispositionCleanup; + } + + // + // Get the mapping + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcPreSetDispositionCleanup; + } + + Mapping = &InstanceContext->Mapping; + + // + // Check to see of this will delete an ancestor of the real mapping. + // + + NcComparePath( &FileInfo->Name, + &Mapping->RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + if (RealOverlap.Ancestor) { + + // + // The file is an ancestor of the real mapping, so disallow setting + // disposition. + // + + Status = STATUS_ACCESS_DENIED; + goto NcPreSetDispositionCleanup; + } + + // + // Check the user mapping. + // + + NcComparePath( &FileInfo->Name, + &Mapping->UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + if (UserOverlap.Ancestor) { + + // + // The file is an ancestor of the user mapping, so disallow setting + // disposition. + // + + Status = STATUS_ACCESS_DENIED; + goto NcPreSetDispositionCleanup; + } + + // + // The file is ok to mark for delete. + // + + Status = STATUS_SUCCESS; + goto NcPreSetDispositionCleanup; + +NcPreSetDispositionCleanup: + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + Data->IoStatus.Status = Status; + + } else { + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + if (FileInfo) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (InstanceContext) { + + FltReleaseContext( InstanceContext ); + } + + return ReturnValue; +} + + +FLT_PREOP_CALLBACK_STATUS +NcPreSetLinkInformation ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Fltmgr callback which manages link creation on a file. + We need to make sure that new links down the user mapping + are redirected to the real mapping. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS ReturnValue; + NTSTATUS Status; + PFILE_LINK_INFORMATION LinkInfo = + Data->Iopb->Parameters.SetFileInformation.InfoBuffer; + PFILE_LINK_INFORMATION MungedLinkInfo = NULL; + ULONG MungedLinkInfoSize; + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NC_PATH_OVERLAP RealOverlap; + NC_PATH_OVERLAP UserOverlap; + UNICODE_STRING UserRemainder; + UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + UNREFERENCED_PARAMETER( CompletionContext ); + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + } + + Status = FltGetDestinationFileNameInformation( FltObjects->Instance, + FltObjects->FileObject, + LinkInfo->RootDirectory, + LinkInfo->FileName, + LinkInfo->FileNameLength, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + } + + // + // Calculate Overlap and Remainder + // + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.UserMapping, + &UserRemainder, + IgnoreCase, + TRUE, + &UserOverlap ); + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + // + // We cannot allow the user to link inside the real mapping, since it + // is hidden. + // + + if (RealOverlap.Match) { + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + + } else if (RealOverlap.InMapping) { + + // + // We should never get here. Getting here requires an + // OPEN_TARGET_DIRECTORY open which should already have failed. + // + + FLT_ASSERT( !RealOverlap.InMapping ); + + Status = STATUS_OBJECT_PATH_NOT_FOUND; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + + } else if ((RealOverlap.Ancestor || UserOverlap.Ancestor) && + LinkInfo->ReplaceIfExists) { + + // + // The user is attempting to overwrite a parent of the mapping. + // Fail this operation. + // + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + } + + // + // If the destination path is outside the mapping then we can pass it + // through without a problem. + // + + if (!UserOverlap.InMapping) { + + // + // The destination outside the mapping. We can ignore this IO. + // + + Status = STATUS_SUCCESS; + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreSetLinkInformationCleanup; + } + + // + // The destination is inside the mapping. This means we have to issue + // our own request and forward the results to the user. + // + + // + // We need to build a new path to link on. + // + + Status = NcConstructPath( &InstanceContext->Mapping.RealMapping, + &UserRemainder, + TRUE, + &MungedName ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + } + + // + // Create our own link structure. + // + + MungedLinkInfoSize = sizeof(FILE_LINK_INFORMATION) + MungedName.Length - sizeof(WCHAR); + MungedLinkInfo = ExAllocatePoolWithTag( PagedPool, + MungedLinkInfoSize, + NC_SET_LINK_BUFFER_TAG ); + + if (MungedLinkInfo == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreSetLinkInformationCleanup; + } + + MungedLinkInfo->ReplaceIfExists = LinkInfo->ReplaceIfExists; + MungedLinkInfo->RootDirectory = NULL; + MungedLinkInfo->FileNameLength = MungedName.Length; + + RtlCopyMemory( &MungedLinkInfo->FileName, MungedName.Buffer, MungedName.Length ); + + // + // Issue our own request. + // + + Status = FltSetInformationFile( FltObjects->Instance, + FltObjects->FileObject, + MungedLinkInfo, + MungedLinkInfoSize, + FileLinkInformation ); + + // + // Because we issued the IO, we will pass complete this ourselves. + // + + ReturnValue = FLT_PREOP_COMPLETE; + +NcPreSetLinkInformationCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + Data->IoStatus.Status = Status; + } + + if (MungedLinkInfo != NULL) { + + ExFreePoolWithTag( MungedLinkInfo, NC_SET_LINK_BUFFER_TAG ); + } + + if (FileInfo != NULL) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (MungedName.Buffer != NULL) { + + ExFreePoolWithTag( MungedName.Buffer, NC_TAG ); + } + + return ReturnValue; +} + +FLT_PREOP_CALLBACK_STATUS +NcPreRename( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Fltmgr callback which manages renaming files. We must disallow renaming + on an ancestor of either mapping because otherwise we would have to + maintain the mapping's short/long name pairings. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + // + // Return Values + // + + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS ReturnValue; + + // + // Contexts + // + + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + + // + // Data + // + + PFILE_RENAME_INFORMATION RenameInfo = + Data->Iopb->Parameters.SetFileInformation.InfoBuffer; + + // + // FileInformation + // + + PFLT_FILE_NAME_INFORMATION TargetInfo = NULL; + PFLT_FILE_NAME_INFORMATION SrcInfo = NULL; + + // + // Target Real Overlap + // + + NC_PATH_OVERLAP TargetRealOverlap; + UNICODE_STRING TargetRealRemainder; + + // + // Target User Overlap + // + + NC_PATH_OVERLAP TargetUserOverlap; + UNICODE_STRING TargetUserRemainder; + + // + // Src Real Overlap + // + + NC_PATH_OVERLAP SrcRealOverlap; + NC_PATH_OVERLAP SrcUserOverlap; + + // + // Munge Data + // + + UNICODE_STRING MungedTargetName = EMPTY_UNICODE_STRING; + PFILE_RENAME_INFORMATION MungedRenameInfo = NULL; + ULONG MungedRenameLength; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( CompletionContext ); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + + // + // Get Instance Context + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + // + // Find out the src file's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &SrcInfo); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + Status = FltParseFileNameInformation( SrcInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + // + // Find the src's overlap with the real and user mappings. + // + + NcComparePath( &SrcInfo->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &SrcRealOverlap ); + + NcComparePath( &SrcInfo->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &SrcUserOverlap ); + + // + // If the src is an ancestor of either the user or real mappings we can + // fail the request. + // + + if (SrcUserOverlap.Ancestor || SrcRealOverlap.Ancestor) { + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_ACCESS_DENIED; + goto NcPreRenameCleanup; + } + + // + // Find out the target file's name. + // + + Status = FltGetDestinationFileNameInformation( FltObjects->Instance, + FltObjects->FileObject, + RenameInfo->RootDirectory, + RenameInfo->FileName, + RenameInfo->FileNameLength, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &TargetInfo); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + Status = FltParseFileNameInformation( TargetInfo ); + + if( !NT_SUCCESS( Status ) ) { + + FLT_ASSERT( NT_SUCCESS( Status ) ); + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + // + // Find the target's overlap with the real and user mappings. + // + + NcComparePath( &TargetInfo->Name, + &InstanceContext->Mapping.RealMapping, + &TargetRealRemainder, + IgnoreCase, + TRUE, + &TargetRealOverlap ); + + NcComparePath( &TargetInfo->Name, + &InstanceContext->Mapping.UserMapping, + &TargetUserRemainder, + IgnoreCase, + TRUE, + &TargetUserOverlap ); + + // + // If the target is in the real mapping, then disallow the rename. If + // the target is to an ancestor of the mappings, this could change IDs + // and is therefore also disallowed. + // + + if (TargetRealOverlap.InMapping) { + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + + } else if ((TargetRealOverlap.Ancestor || TargetUserOverlap.Ancestor) && + RenameInfo->ReplaceIfExists) { + + Status = STATUS_ACCESS_DENIED; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + + // + // If the target is in the user mapping, then we need to munge the + // name and send the request down. If this is a stream rename we + // do not perform the mapping since only the stream name is changing. + // + + if (TargetUserOverlap.InMapping && + (RenameInfo->FileName[0] != ':')) { + + Status = NcConstructPath( &InstanceContext->Mapping.RealMapping, + &TargetUserRemainder, + TRUE, + &MungedTargetName ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + + // + // Because the target is in the user mapping, we have to issue + // our own rename down the real mapping. + // + + // + // Allocate rename information structure. + // + + MungedRenameLength = sizeof(FILE_RENAME_INFORMATION) - + sizeof(WCHAR) + + MungedTargetName.Length; + + MungedRenameInfo = ExAllocatePoolWithTag( PagedPool, + MungedRenameLength, + NC_RENAME_BUFFER_TAG ); + + if (MungedRenameInfo == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + } + + // + // Copy user rename parameters. + // + + MungedRenameInfo->ReplaceIfExists = RenameInfo->ReplaceIfExists; + MungedRenameInfo->RootDirectory = NULL; + MungedRenameInfo->FileNameLength = MungedTargetName.Length; + RtlCopyMemory( &MungedRenameInfo->FileName, + MungedTargetName.Buffer, + MungedTargetName.Length ); + + // + // Send the request. Note that we cannot just place the new buffer + // in the CallbackData; filesystems use the name from a previous + // OPEN_TARGET_DIRECTORY open, so changing the buffer here would + // result in unexpected (and undefined!) behavior. + // + + Status = FltSetInformationFile( FltObjects->Instance, + FltObjects->FileObject, + MungedRenameInfo, + MungedRenameLength, + FileRenameInformation ); + + // + // Complete the IO. + // + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreRenameCleanup; + + } else { + + // + // The target was outside the mapping. The rename does not have + // to be munged. Pass through. + // + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreRenameCleanup; + + } + +NcPreRenameCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + Data->IoStatus.Status = Status; + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (TargetInfo != NULL) { + + FltReleaseFileNameInformation( TargetInfo ); + } + + if (SrcInfo != NULL) { + + FltReleaseFileNameInformation( SrcInfo ); + } + + if (MungedTargetName.Buffer != NULL) { + + ExFreePoolWithTag( MungedTargetName.Buffer, NC_GENERATE_NAME_TAG ); + } + + if (MungedRenameInfo != NULL) { + + ExFreePoolWithTag( MungedRenameInfo, NC_RENAME_BUFFER_TAG ); + } + + return ReturnValue; +} + + diff --git a/filesys/miniFilter/NameChanger/ncfsctrl.c b/filesys/miniFilter/NameChanger/ncfsctrl.c new file mode 100644 index 00000000..32752810 --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncfsctrl.c @@ -0,0 +1,3634 @@ +/*++ + +Copyright (c) 2008 - 2009 Microsoft Corporation + +Module Name: + + ncfsctrl.c + +Abstract: + + Contains routines to process user-initiated file system control + (FSCTL) requests. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +NTSTATUS +NcFindFilesBySidTranslateBuffers ( + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN IgnoreCase, + _In_ PUNICODE_STRING UserRequestName, + _In_ PUNICODE_STRING OpenedName, + _In_ PFILE_NAME_INFORMATION InputSystemBuffer, + _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NAME_INFORMATION OutputUserBuffer, + _In_ ULONG InputBufferLength, + _In_ ULONG OutputBufferLength, + _Out_ PULONG InputBufferConsumed, + _Out_ PULONG OutputBufferWritten, + _In_ BOOLEAN ReturnRealMappingPaths + ) ; + +NTSTATUS +NcUsnTranslateBuffers ( + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN IgnoreCase, + _In_ ULONGLONG RealMappingParentId, + _In_ ULONGLONG UserMappingParentId, + _In_ PUSN_RECORD InputSystemBuffer, + _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PUSN_RECORD OutputUserBuffer, + _In_ ULONG InputBufferLength, + _In_ ULONG OutputBufferLength, + _Out_ PULONG InputBufferConsumed, + _Out_ PULONG OutputBufferWritten + ); + +VOID +NcPostReadUsnJournalWorker ( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID Context + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcFindFilesBySidTranslateBuffers) +#pragma alloc_text(PAGE, NcStreamHandleContextFindBySidCreate) +#pragma alloc_text(PAGE, NcStreamHandleContextFindBySidClose) +#pragma alloc_text(PAGE, NcPreFindFilesBySid) +#pragma alloc_text(PAGE, NcPostFindFilesBySid) +#if FLT_MGR_WIN7 +#pragma alloc_text(PAGE, NcPostLookupStreamFromCluster) +#endif +#pragma alloc_text(PAGE, NcUsnTranslateBuffers) +#pragma alloc_text(PAGE, NcPostEnumUsnData) +#pragma alloc_text(PAGE, NcPostReadUsnJournalWorker) +#pragma alloc_text(PAGE, NcPostReadFileUsnData) +#endif + +NTSTATUS +NcStreamHandleContextFindBySidCreate ( + _Out_ PNC_FIND_BY_SID_CONTEXT Context + ) +/*++ + +Routine Description: + + This function is called to initialize the find files by SID portion of + a stream handle context. + +Arguments: + + Context - Pointer to the find files by SID portion of the stream handle + context. + +Return Value: + + Returns the status of the operation (currently only STATUS_SUCCESS.) + +--*/ +{ + + PAGED_CODE(); + + Context->RealHandle = NULL; + Context->RealFileObject = NULL; + + Context->BufferToFree = NULL; + Context->BufferSize = 0; + Context->CurrentEntry = 0; + + Context->OutstandingRequests = 0; + + return STATUS_SUCCESS; + +} + +VOID +NcStreamHandleContextFindBySidClose ( + _In_ PNC_FIND_BY_SID_CONTEXT Context + ) +/*++ + +Routine Description: + + This function is called to tear down the find files by SID portion of + a stream handle context. + +Arguments: + + Context - Pointer to the find files by SID portion of the stream handle + context. + +Return Value: + + None. + +--*/ +{ + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // This assert is overactive. There are cases which we cannot + // prevent which can cause it to fire (eg. we successfully + // sent a request down but failed on completion before being able + // to obtain/lock the handle context.) The effect of leaving + // this nonzero is that we will not clean up the handle and + // file object until the user's handle is closed. We are prepared + // to live with that (and the user could generate this condition + // herself by ceasing issuing queries before the query is fully + // complete.) + // + + FLT_ASSERT( Context->OutstandingRequests == 0 ); + + if (Context->RealHandle != NULL) { + + FltClose( Context->RealHandle ); + Context->RealHandle = NULL; + } + + if (Context->RealFileObject != NULL) { + + ObDereferenceObject( Context->RealFileObject ); + Context->RealFileObject = NULL; + } + + if (Context->BufferToFree != NULL) { + + ExFreePoolWithTag( Context->BufferToFree, NC_TAG ); + Context->BufferToFree = NULL; + Context->BufferSize = 0; + Context->CurrentEntry = 0; + } + + FLT_ASSERT( Context->BufferSize == 0 && Context->CurrentEntry == 0 ); + +} + +NTSTATUS +NcFindFilesBySidTranslateBuffers ( + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN IgnoreCase, + _In_ PUNICODE_STRING UserRequestName, + _In_ PUNICODE_STRING OpenedName, + _In_ PFILE_NAME_INFORMATION InputSystemBuffer, + _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NAME_INFORMATION OutputUserBuffer, + _In_ ULONG InputBufferLength, + _In_ ULONG OutputBufferLength, + _Out_ PULONG InputBufferConsumed, + _Out_ PULONG OutputBufferWritten, + _In_ BOOLEAN ReturnRealMappingPaths + ) +/*++ + +Routine Description: + + This routine is used to transform buffers from a filesystem view to the + user view. Depending on flags we either replace references to the real + mapping with the user mapping or suppress all entries from the real + mapping. + +Arguments: + + InstanceContext - Pointer to the context describing this instance of the + filter. + + IgnoreCase - TRUE if comparisons should be case insensitive, FALSE if + comparisons should be case sensitive. + + UserRequestName - The string for the path that the user opened and is + finding child files by SID on. + + OpenedName - The string for the path that we are processing child files + on. "Typically" the same as UserRequestName, but may be different if + we are merging results from the user's handle with those of the + real mapping. In this case, OpenedName may refer to the path to + the real mapping. + + InputSystemBuffer - The buffer we are processing from. Note that this + routine assumes the buffer is not volatile (cannot be externally + modified.) For this reason, the buffer is expected to be system + buffered by the caller if it is not already. + + Note however that the contents of the buffer may have originated from + a user buffer (e.g. via a memcpy), so although the contents are non-volatile, + they are not to be trusted. + + OutputUserBuffer - The buffer we are returning munged results into. + This buffer is expected to have been probed, and this function will + catch and return any invalid buffer exceptions. + + InputBufferLength - Size, in bytes, of the input buffer. + + OutputBufferLength - Size, in bytes, of the output buffer. + + InputBufferConsumed - Pointer to a ULONG which will contain, on output, + the number of bytes processed from the input buffer. This may be + zero, the length of the input buffer, or any value in between. + This value is undefined on failure. + + OutputBufferWritten - Pointer to a ULONG which will contain, on output, + the number of bytes written into the output buffer. This may be + zero, the length of the output buffer, or any value in between. + This value is undefined on failure. + + ReturnRealMappingPaths - TRUE if this function should transform and + return any paths that are within the real mapping. If FALSE, we + omit these entries. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + NC_PATH_OVERLAP RealOverlap; + + // + // Pointers into the above buffers used as we read entries from + // one buffer and write them to the other. + // + + PFILE_NAME_INFORMATION SourceEntry = NULL; + PFILE_NAME_INFORMATION DestEntry = NULL; + + // + // The name of the object opened by the user; a full path to the path + // returned from the filesystem that we are currently examining; the + // Remainder of that path if it turns out to be in a mapping; the + // transformed name if one is required. + // + + UNICODE_STRING NameString = EMPTY_UNICODE_STRING; + UNICODE_STRING Remainder; + UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; + + // + // A pointer to one of the above buffers that we intend on returning + // to the application. Note that this may be NULL if an entry is + // being suppressed. + // + + PUNICODE_STRING ReturnName; + ULONG EntryLength; + + ULONG UlongResult; + PVOID PointerResult; + + PAGED_CODE(); + + SourceEntry = InputSystemBuffer; + DestEntry = OutputUserBuffer; + *InputBufferConsumed = 0; + *OutputBufferWritten = 0; + + // + // This routine assumes it will only be called if there is work to do. + // + + FLT_ASSERT( InputBufferLength && OutputBufferLength ); + + try { + + while( SourceEntry ) { + + // + // The path returned is relative to the handle used to request + // the service. We now allocate and construct a full path name. + // Use safe math routines when consulting the buffer, since it is + // possible that the buffer contents were modified by malicious + // code before getting here. + // + + EntryLength = OpenedName->Length + + sizeof(WCHAR); + + if (EntryLength >= MAXUSHORT) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcFindFilesBySidTranslateBuffersCleanup; + } + + Status = RtlULongAdd( EntryLength, + SourceEntry->FileNameLength, + &EntryLength ); + + if (!NT_SUCCESS( Status ) || + (EntryLength >= MAXUSHORT)) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcFindFilesBySidTranslateBuffersCleanup; + } + + Status = RtlULongAdd( *InputBufferConsumed, + FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ), + &UlongResult ); + + if (NT_SUCCESS( Status )) { + + Status = RtlULongAdd( UlongResult, + SourceEntry->FileNameLength, + &UlongResult ); + } + + if (!NT_SUCCESS( Status ) || + (UlongResult > InputBufferLength)) { + + // + // We have an entry that walks off the end of the buffer. + // Since the buffer that we get from the filesystem is not + // system buffered, we cannot guarantee that a caller is not + // corrupting it in an in-flight request. This condition + // should never occur without aforementioned corruption. + // + + FLT_ASSERT( FALSE ); + Status = STATUS_INVALID_USER_BUFFER; + goto NcFindFilesBySidTranslateBuffersCleanup; + + } + + if (EntryLength > NameString.MaximumLength) { + + if (NameString.Buffer != NULL) { + NcFreeUnicodeString( &NameString ); + } + + NameString.Buffer = ExAllocatePoolWithTag( PagedPool, + EntryLength, + NC_TAG ); + + if (NameString.Buffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcFindFilesBySidTranslateBuffersCleanup; + } + + NameString.MaximumLength = (USHORT)EntryLength; + } + + _Analysis_assume_(NameString.Buffer != NULL); + RtlCopyMemory( NameString.Buffer, + OpenedName->Buffer, + OpenedName->Length ); + + NameString.Buffer[OpenedName->Length / sizeof(WCHAR)] = NC_SEPARATOR; + + RtlCopyMemory( Add2Ptr( NameString.Buffer, OpenedName->Length + sizeof(WCHAR) ), + SourceEntry->FileName, + SourceEntry->FileNameLength ); + + NameString.Length = (USHORT)EntryLength; + + // + // Now see if this name needs to be munged. + // + + NcComparePath( &NameString, + &InstanceContext->Mapping.RealMapping, + &Remainder, + IgnoreCase, + TRUE, + &RealOverlap ); + + if (RealOverlap.InMapping && ReturnRealMappingPaths) { + + // + // If the name is under the real mapping and we're returning + // paths under the real mapping, the query is a common + // ancestor of both real and user mappings. In this case, we + // need to translate real mapping paths to user mapping paths. + // + + Status = NcConstructPath( &InstanceContext->Mapping.UserMapping, + &Remainder, + TRUE, + &MungedName ); + + if (!NT_SUCCESS( Status )) { + goto NcFindFilesBySidTranslateBuffersCleanup; + } + + ReturnName = &MungedName; + + } else if (RealOverlap.InMapping) { + + // + // If the name is in the real mapping but the caller is asking + // for a tree which is not an ancestor of the user mapping, + // then this name should not be visible under this tree. + // + + ReturnName = NULL; + + } else { + + ReturnName = &NameString; + + } + + if (ReturnName != NULL) { + + // + // Since this API returns paths under the one the query was + // issued on, the path we have now had better be longer than + // that path length. Even if we refer to the same object, + // our path includes a trailing slash. + // + + FLT_ASSERT( ReturnName->Length > UserRequestName->Length ); + + // + // We take care not to copy the leading slash. If we have + // a trailing slash as well as a leading slash, truncate the + // string. This can occur when we're injecting an entry for + // the mapping. + // + + if (ReturnName->Length - UserRequestName->Length > sizeof(WCHAR) && + ReturnName->Buffer[ReturnName->Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { + + ReturnName->Length -= sizeof(WCHAR); + } + + EntryLength = AlignToSize( FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ) + ReturnName->Length - UserRequestName->Length - sizeof(WCHAR), 8); + + // + // We've done all we can. Return now to let our caller deal + // with the remaining buffer. + // + + Status = RtlULongAdd( EntryLength, + *OutputBufferWritten, + &UlongResult ); + + if (!NT_SUCCESS( Status ) || + (UlongResult > OutputBufferLength)) { + + SourceEntry = NULL; + DestEntry = NULL; + break; + } + + // + // Copy the relative path name, taking care to exclude the + // initial slash. + // + + DestEntry->FileNameLength = ReturnName->Length - UserRequestName->Length - sizeof(WCHAR); + RtlCopyMemory( DestEntry->FileName, + Add2Ptr( ReturnName->Buffer, UserRequestName->Length + sizeof(WCHAR)), + ReturnName->Length - UserRequestName->Length - sizeof(WCHAR)); + + // + // Advance the destination that we're writing new entries by + // however much we just consumed. + // + + *OutputBufferWritten += EntryLength; + DestEntry = Add2Ptr( DestEntry, EntryLength ); + + if (MungedName.Buffer != NULL) { + ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); + MungedName.Buffer = NULL; + } + } + + // + // Now calculate and advance the location we're reading and + // processing from. If we're have no more buffer left, we're + // done. + // + + EntryLength = AlignToSize( FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ) + SourceEntry->FileNameLength, 8 ); + + PointerResult = Add2Ptr( SourceEntry, EntryLength ); + + Status = RtlULongAdd( *InputBufferConsumed, + EntryLength, + InputBufferConsumed ); + + // + // There's a problem if one of the following happened: + // + // 1) We overflowed when accounting for for consumed input buffer + // 2) We wrapped when advancing SourceEntry + // 3) PointerResult is not within InputSystemBuffer + // + + if (!NT_SUCCESS( Status ) || + (PointerResult < (PVOID)SourceEntry) || + (PointerResult < Add2Ptr( InputSystemBuffer, sizeof(FILE_NAME_INFORMATION) ))) { + + FLT_ASSERT( FALSE ); + + Status = STATUS_INVALID_USER_BUFFER; + goto NcFindFilesBySidTranslateBuffersCleanup; + } + + SourceEntry = (PFILE_NAME_INFORMATION)PointerResult; + + // + // If we've just advanced our next location beyond the end of the + // input buffer, or there isn't enough room in it for even a FILE_NAME_INFORMATION + // structure, terminate the loop by setting SourceEntry to NULL so + // we can at least return the valid entries we have. + // + + FLT_ASSERT( *InputBufferConsumed <= InputBufferLength ); + + if ((*InputBufferConsumed >= InputBufferLength) || + (*InputBufferConsumed + FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ) > InputBufferLength)) { + + // + // Indicate to the caller that we consumed exactly the input buffer. + // Otherwise it may hold the possibly small remnant and come back + // in to this routine later with that remnant, causing us to overread + // the buffer. + // + + *InputBufferConsumed = InputBufferLength; + SourceEntry = NULL; + } + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + +NcFindFilesBySidTranslateBuffersCleanup: + + if (MungedName.Buffer != NULL) { + + ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); + } + + if (NameString.Buffer != NULL) { + + NcFreeUnicodeString( &NameString ); + } + + return Status; + +} + +FLT_PREOP_CALLBACK_STATUS +NcPreFindFilesBySid ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Routine is invoked when the user wants to query files owned by + a particular SID under a subtree. For this call, we must return + paths relative to the user's handle. + + Implementationally, we have four cases: + + 1. If the handle is an ancestor or match of the real mapping, we may + need to remove entries. + + 2. If the handle is an ancestor or match of the user mapping, we may + need to insert entries. We implement this in two phases: first, + we enumerate all results under the user's handle; when complete, + we open a handle to the mapping and enumerate all results under + that. + + 3. If the handle is an ancestor of both, we may need to transform + entries. + + 4. If the handle is not an ancestor of either, we have no work to do. + + For cases 2 and 3, the size of the data may change. Since we can't + omit entries, we add those to a buffer, then return (and process) + entries from the buffer next time this function is invoked on the + handle. Only when we have nothing buffered do we continue sending + requests to the filesystem. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the file object we are enumerating + under for find files by SID. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS ReturnValue = FLT_PREOP_SYNCHRONIZE; + NC_PATH_OVERLAP RealOverlap, UserOverlap; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + PFLT_FILE_NAME_INFORMATION FileInfoInternalHandle = NULL; + PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; + PNC_FIND_BY_SID_CONTEXT FindBySidCtx = NULL; + ULONG SizeWeReturn = 0; + BOOLEAN UnlockContext = FALSE; + + // + // The name of the object opened by the user, and the path of the object + // we're issuing queries on. These are typically the same, but will + // differ when we're injecting entries (user opens an ancestor of the + // user mapping, but we're sending queries to the real mapping.) + // + + UNICODE_STRING UserRequestName; + UNICODE_STRING OpenedName; + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + if (Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength < sizeof( FILE_NAME_INFORMATION )) { + + // + // This somewhat strange code is used to maintain parity with the + // filesystem. + // + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_INVALID_USER_BUFFER; + goto NcPreFindFilesBySidCleanup; + } + + // + // Get our instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreFindFilesBySidCleanup; + } + + // + // Get the file's name. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreFindFilesBySidCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreFindFilesBySidCleanup; + } + + // + // If the path that was used for the query ends in a trailing slash + // (ie., the root directory), shave it off here. This keeps our logic + // simple, rather than dealing with this over and over. + // + + UserRequestName = FileInfo->Name; + FLT_ASSERT( UserRequestName.Length > 0 ); + if (UserRequestName.Buffer[UserRequestName.Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { + UserRequestName.Length -= sizeof(WCHAR); + } + + // + // As an optimization, check if the handle queried on is an ancestor of + // either mapping. Cases to consider: + // + // 1. If the handle is an ancestor or match of the real mapping, we may + // need to remove entries. + // + // 2. If the handle is an ancestor or match of the user mapping, we may + // need to insert entries. + // + // 3. If the handle is an ancestor of both, we may need to transform + // entries. + // + // 4. If the handle is not an ancestor of either, we have no work to + // do. As an optimization, we can stop now if this is the case. + // + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + if (!(UserOverlap.Ancestor || RealOverlap.Ancestor)) { + + // + // In case 4 above, leave now. + // + + ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + goto NcPreFindFilesBySidCleanup; + } + + Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, + FltObjects->Instance, + FltObjects->FileObject, + &HandleContext ); + + if (!NT_SUCCESS( Status )) { + + ReturnValue = FLT_PREOP_COMPLETE; + goto NcPreFindFilesBySidCleanup; + } + + FLT_ASSERT( HandleContext != NULL ); + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + FindBySidCtx = &HandleContext->FindBySidContext; + + // + // Typically we query on the user's file object. We change file + // objects if the query is an ancestor of the mapping and we need to + // query the mapping itself. We will need to translate names when + // this happens. Since we cannot hold locks calling into the + // filesystem and don't want to drop locks during the calculation + // below, build a name for the mapping (pessimistically!) now. + // + // We only really need this if BufferToFree != NULL, but this may + // change as soon as we drop the lock. + // + + if (FindBySidCtx->RealFileObject == NULL) { + + OpenedName = UserRequestName; + + } else { + + PFILE_OBJECT RealFileObject = FindBySidCtx->RealFileObject; + + // + // Drop the lock. Note that there are two possibilities for the + // FileObject - it either refers to the mapping, or it is NULL to + // indicate that we do not need to process the mapping, are not + // yet processing the mapping, or we are done with the operation. + // + // In dropping the lock, it is possible that the FileObject to go + // to NULL. However, since we know it was non-NULL, the only + // possibility if this occurs is that we are done with the + // enumeration. + // + + ObReferenceObject( RealFileObject ); + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + Status = NcGetFileNameInformation( NULL, + RealFileObject, + FltObjects->Instance, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfoInternalHandle ); + + if (!NT_SUCCESS( Status )) { + + ObDereferenceObject( RealFileObject ); + goto NcPreFindFilesBySidCleanup; + } + + Status = FltParseFileNameInformation( FileInfoInternalHandle ); + + if (!NT_SUCCESS( Status )) { + + ObDereferenceObject( RealFileObject ); + goto NcPreFindFilesBySidCleanup; + } + + OpenedName = FileInfoInternalHandle->Name; + FLT_ASSERT( OpenedName.Length > 0 ); + if (OpenedName.Buffer[OpenedName.Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { + OpenedName.Length -= sizeof(WCHAR); + } + + // + // This may trigger a close, and must be done before we acquire the + // lock. + // + + ObDereferenceObject( RealFileObject ); + + // + // Now reacquire the lock so we can detect if we have a leftover + // buffer to munge and munge it safely. Check whether the + // enumeration is still ongoing or if we are already complete. + // + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + if (FindBySidCtx->RealFileObject == NULL) { + + FLT_ASSERT( FindBySidCtx->BufferToFree == NULL && + FindBySidCtx->OutstandingRequests == 0 ); + + ReturnValue = FLT_PREOP_COMPLETE; + Status = STATUS_SUCCESS; + SizeWeReturn = 0; + + goto NcPreFindFilesBySidCleanup; + } + } + + if (FindBySidCtx->BufferToFree != NULL) { + + // + // The user's buffer that we will be writing to + // + + PFILE_NAME_INFORMATION DestBuffer = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer; + ULONG BufferSize = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength; + + ULONG InputConsumed; + + FLT_ASSERT( FindBySidCtx->BufferSize > 0 && + FindBySidCtx->CurrentEntry < FindBySidCtx->BufferSize ); + + // + // Whether we succeed or fail, we don't intend to let this request + // go to the filesystem if we have data that has not been returned + // yet. + // + + ReturnValue = FLT_PREOP_COMPLETE; + + // + // If a driver above us has posted the request, we may need to + // map a system buffer for the call from its Mdl. If not, we can + // use the user address passed in to the call. Be sure to check + // the "user" address really does point to user memory. + // + + if (Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress != NULL) { + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + goto NcPreFindFilesBySidCleanup; + } + + DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (DestBuffer == NULL) { + + Status = STATUS_NO_MEMORY; + goto NcPreFindFilesBySidCleanup; + } + } else { + + try { + + if (Data->RequestorMode != KernelMode) { + ProbeForWrite( DestBuffer, BufferSize, sizeof( UCHAR )); + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS( Status )) { + + goto NcPreFindFilesBySidCleanup; + } + } + + // + // Translate the strings in the buffer by applying the parent's path + // (either the opened handle's path or the mapping's path), then + // change the mapping as necessary. + // + + Status = NcFindFilesBySidTranslateBuffers( InstanceContext, + IgnoreCase, + &UserRequestName, + &OpenedName, + Add2Ptr( FindBySidCtx->BufferToFree, FindBySidCtx->CurrentEntry ), + DestBuffer, + FindBySidCtx->BufferSize - FindBySidCtx->CurrentEntry, + BufferSize, + &InputConsumed, + &SizeWeReturn, + (BOOLEAN)UserOverlap.Ancestor ); + + if (!NT_SUCCESS( Status )) { + + goto NcPreFindFilesBySidCleanup; + } + + FindBySidCtx->CurrentEntry += InputConsumed; + FLT_ASSERT( FindBySidCtx->CurrentEntry <= FindBySidCtx->BufferSize ); + + if (FindBySidCtx->CurrentEntry >= FindBySidCtx->BufferSize) { + + // + // We've finished processing the leftover buffer. Tear it down + // now. Note that we will not attempt to call into the + // filesystem and fully pack the buffer; so long as we return + // something, that's good enough. + // + + ExFreePoolWithTag( FindBySidCtx->BufferToFree, NC_TAG ); + FindBySidCtx->BufferToFree = NULL; + FindBySidCtx->CurrentEntry = 0; + FindBySidCtx->BufferSize = 0; + } + + if (InputConsumed == 0) { + + FLT_ASSERT( SizeWeReturn == 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + + } else if (FindBySidCtx->RealFileObject) { + + // + // We don't have a buffer, but we do have a file object to redirect + // to. This implies that we've finished reporting on the user's + // handle, and are now reporting on a redirected handle. For + // simplicity, switch the file objects used by this request and + // send it down. + // + + Data->Iopb->TargetFileObject = FindBySidCtx->RealFileObject; + FltSetCallbackDataDirty( Data ); + + // + // Pass a note back to ourselves about which file we're querying + // on. + // + + *CompletionContext = FindBySidCtx->RealFileObject; + + // + // If we're sending down this request to the filesystem, bump the + // outstanding request count. + // + + FLT_ASSERT( UnlockContext ); + FindBySidCtx->OutstandingRequests++; + + FLT_ASSERT( ReturnValue == FLT_PREOP_SYNCHRONIZE ); + + } else { + + // + // Pass a note back to ourselves about which file we're querying + // on. + // + + *CompletionContext = Data->Iopb->TargetFileObject; + + // + // If we're sending down this request to the filesystem, bump the + // outstanding request count. + // + + FLT_ASSERT( UnlockContext ); + FindBySidCtx->OutstandingRequests++; + + FLT_ASSERT( ReturnValue == FLT_PREOP_SYNCHRONIZE ); + + } + +NcPreFindFilesBySidCleanup: + + if (ReturnValue == FLT_PREOP_COMPLETE) { + + Data->IoStatus.Status = Status; + + if (NT_SUCCESS( Status )) { + + Data->IoStatus.Information = SizeWeReturn; + } else { + + Data->IoStatus.Information = 0; + } + } + + if (UnlockContext) { + + FLT_ASSERT( HandleContext != NULL ); + NcUnlockStreamHandleContext( HandleContext ); + + } + + if (FileInfo != NULL) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (FileInfoInternalHandle != NULL) { + + FltReleaseFileNameInformation( FileInfoInternalHandle ); + } + + if (HandleContext != NULL) { + + FltReleaseContext( HandleContext ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + return ReturnValue; +} + + + +FLT_POSTOP_CALLBACK_STATUS +NcPostFindFilesBySid ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + Routine is invoked when the user wants to query files owned by + a particular SID under a subtree. For this call, we must return + paths relative to the user's handle. + + This routine must consider the following three cases: + + 1. If the handle is an ancestor or match of the real mapping, we may + need to remove entries. + + 2. If the handle is an ancestor or match of the user mapping, we may + need to insert entries. We implement this in two phases: first, + we enumerate all results under the user's handle; when complete, + we open a handle to the mapping and enumerate all results under + that. + + 3. If the handle is an ancestor of both, we may need to transform + entries. + + Logically, we take the data returned from the filesystem (if any), + transform the buffers, and return it to the user. If we have more than + we can return, we add it to our leftover buffer. If we don't have any + data from the filesystem, we check if we have any data left over in the + leftover buffer, transform and return that. If we don't have any data + in the leftover buffer, we check if we need to enumerate from the + mapping handle (in number 2 above), and if so, open and query that, + transform the results, and return. If we've already done that and + still have no data, we close our handles, and return an empty buffer + to the caller, indicating the series of requests is complete. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the file object we are enumerating + under for find files by SID. NcPreFindFilesBySid ensures that this + will never be NULL if the post-op is invoked. + + Flags - The flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NTSTATUS Status; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; + PNC_FIND_BY_SID_CONTEXT FindBySidCtx = NULL; + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + PFLT_FILE_NAME_INFORMATION FileInfoInternalHandle = NULL; + + // + // The FileObject that this request was sent to. + // + + PFILE_OBJECT FileObject = (PFILE_OBJECT)CompletionContext; + + // + // The user's buffer that we will be writing to, and the system copy + // of the data returned by the file system which we will read from. + // + + PFILE_NAME_INFORMATION DestBuffer = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer; + PFILE_NAME_INFORMATION SourceBuffer = NULL; + ULONG SourceBufferSize; + + // + // The opened path's relation to the mappings. + // + + NC_PATH_OVERLAP RealOverlap; + NC_PATH_OVERLAP UserOverlap; + + // + // Size returned from the filesystem to us, size of the user's buffer + // that we can legitimately write to, the amount we actually wrote into + // the user's buffer, and the amount of the filesystem's buffer we have + // processed so far. + // + + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BufferSize = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength; + ULONG InputConsumed; + ULONG SizeWeReturn; + + // + // The name of the object opened by the user, and the path of the object + // we're issuing queries on. These are typically the same, but will + // differ when we're injecting entries (user opens an ancestor of the + // user mapping, but we're sending queries to the real mapping.) + // + + UNICODE_STRING UserRequestName; + UNICODE_STRING OpenedName; + + BOOLEAN InjectionRequired = FALSE; + BOOLEAN UnlockContext = FALSE; + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the operation has already failed, we're not interested in attempting + // to process it. + // + + if (!NT_SUCCESS( Data->IoStatus.Status )) { + + Status = Data->IoStatus.Status; + goto NcPostFindFilesBySidCleanup; + } + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + FLT_ASSERT( BufferSize >= SizeActuallyReturned ); + if (BufferSize < sizeof( FILE_NAME_INFORMATION )) { + + // + // This somewhat strange code is used to maintain parity with the + // filesystem. Note that we checked this in pre, so this check + // should be redundant. + // + + FLT_ASSERT( BufferSize >= sizeof( FILE_NAME_INFORMATION )); + Status = STATUS_INVALID_USER_BUFFER; + goto NcPostFindFilesBySidCleanup; + } + + // + // Get the file handle's name. Names returned via this FSCTL are + // relative to the handle used to obtain them. We will need to + // construct names by combining these values. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + // + // As an optimization, check if the handle queried on is an + // ancestor of either mapping. Cases to consider: + // + // 1. If the handle is an ancestor or match of the real + // mapping, we may need to remove entries. + // + // 2. If the handle is an ancestor or match of the user + // mapping, we may need to insert entries. + // + // 3. If the handle is an ancestor of both, we may need + // to transform entries. + // + // 4. If the handle is not an ancestor of either, we have + // no work to do. As an optimization, we can stop now + // if this is the case. + // + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.RealMapping, + NULL, + IgnoreCase, + TRUE, + &RealOverlap ); + + if (!(UserOverlap.Ancestor || RealOverlap.Ancestor)) { + + // + // If we are not an ancestor, our post callback should + // not have been invoked. + // + + FLT_ASSERT( UserOverlap.Ancestor || RealOverlap.Ancestor ); + + // + // In case 4 above, leave now. + // + + Status = STATUS_SUCCESS; + goto NcPostFindFilesBySidCleanup; + } + + // + // If the path that was used for the query ends in a trailing slash + // (ie., the root directory), shave it off here. This keeps our logic + // simple, rather than dealing with this over and over. + // + + UserRequestName = FileInfo->Name; + FLT_ASSERT( UserRequestName.Length > 0 ); + if (UserRequestName.Buffer[UserRequestName.Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { + UserRequestName.Length -= sizeof(WCHAR); + } + + if (UserOverlap.Ancestor && !RealOverlap.Ancestor) { + + InjectionRequired = TRUE; + } + + Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, + FltObjects->Instance, + FltObjects->FileObject, + &HandleContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + FLT_ASSERT( HandleContext != NULL ); + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + FindBySidCtx = &HandleContext->FindBySidContext; + + // + // If we're being notified of a filesystem completing a request, we'd + // better have seen it. + // + + FLT_ASSERT( FindBySidCtx->OutstandingRequests > 0 ); + + // + // If another driver has posted the request, we may need to map a + // system buffer for the call from its Mdl. If not, we can use the + // user address passed in to the call. Be sure to check the "user" + // address really does point to user memory. + // + + if (Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress != NULL) { + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (DestBuffer == NULL) { + + Status = STATUS_NO_MEMORY; + goto NcPostFindFilesBySidCleanup; + } + + } else { + + try { + + if (Data->RequestorMode != KernelMode) { + ProbeForWrite( DestBuffer, BufferSize, sizeof( UCHAR )); + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + } + +NcPostFindFilesBySidMungeBuffer: + + // + // In the typical case, we're querying on the user's object. In that + // case, the name we're querying on is the same name that will be the + // basis for returned results. + // + + if (FileObject != FindBySidCtx->RealFileObject) { + + OpenedName = UserRequestName; + + } else { + + if (UnlockContext) { + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + } + + // + // We should query this only once, either because we just opened this + // object and are looping, or because it was previously set up and + // we do not loop. If we are looping, we did not reacquire the lock, + // and did not need to drop it above. + // + + FLT_ASSERT( FileInfoInternalHandle == NULL ); + + Status = NcGetFileNameInformation( NULL, + FindBySidCtx->RealFileObject, + FltObjects->Instance, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfoInternalHandle ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + Status = FltParseFileNameInformation( FileInfoInternalHandle ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + OpenedName = FileInfoInternalHandle->Name; + FLT_ASSERT( OpenedName.Length > 0 ); + if (OpenedName.Buffer[OpenedName.Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { + OpenedName.Length -= sizeof(WCHAR); + } + + } + + if (!UnlockContext) { + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + } + + // + // If the filesystem returned something, go ahead and try to munge it. + // + + if (SizeActuallyReturned > sizeof( FILE_NAME_INFORMATION ) - sizeof(WCHAR)) { + + // + // Allocate a new buffer and copy the contents. Note that this is + // particularly important with this call, since it's not system + // buffered; the contents are free to change underneath us. This + // allocation protects us against that, but we still must be + // paranoid touching the buffer, since we cannot trust that it has + // any integrity at this point. + // + // Make sure we allocate enough space to deal with this request, + // plus any leftover buffer. If we don't process this entire buffer + // right now, we'll need to store our remnants with accumulated + // remnants. + // + // The #pragma is a notation to the static code analyzer to not worry + // that we're apparently leaking SourceBuffer. It gets put in to + // FindBySidCtx, which is a part of our stream handle context. When + // the stream handle context is eventually torn down this allocated + // memory will be freed. Naturally you must take care that on any + // paths where SourceBuffer doesn't make it in to the stream handle + // context you deallocate SourceBuffer before this routine exits. + // + + Status = RtlULongAdd( SizeActuallyReturned, + FindBySidCtx->BufferSize, + &SourceBufferSize ); + + if (NT_SUCCESS( Status )) { + + Status = RtlULongSub( SourceBufferSize, + FindBySidCtx->CurrentEntry, + &SourceBufferSize ); + } + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + +#pragma warning(suppress: __WARNING_MEMORY_LEAK) + SourceBuffer = ExAllocatePoolWithTag( PagedPool, + SourceBufferSize, + NC_TAG ); + + if (SourceBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostFindFilesBySidCleanup; + } + + try { + + RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + goto NcPostFindFilesBySidCleanup; + } + + Status = NcFindFilesBySidTranslateBuffers( InstanceContext, + IgnoreCase, + &UserRequestName, + &OpenedName, + SourceBuffer, + DestBuffer, + SizeActuallyReturned, + BufferSize, + &InputConsumed, + &SizeWeReturn, + (BOOLEAN)UserOverlap.Ancestor ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + if (InputConsumed == 0) { + FLT_ASSERT( SizeWeReturn == 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + + if (InputConsumed < SizeActuallyReturned) { + + ULONG RemnantsSize = SizeActuallyReturned; + + // + // We have successfully processed a portion of the buffer, + // but more remains. Append any previously leftover portion + // of buffer to our own, and attach the combined remnants to + // our handle context for later processing. Importantly, + // don't free the buffer here. + // + + if (FindBySidCtx->BufferToFree != NULL) { + + RtlCopyMemory( Add2Ptr( SourceBuffer, SizeActuallyReturned ), + Add2Ptr( FindBySidCtx->BufferToFree, FindBySidCtx->CurrentEntry ), + FindBySidCtx->BufferSize - FindBySidCtx->CurrentEntry ); + + RemnantsSize += FindBySidCtx->BufferSize - FindBySidCtx->CurrentEntry; + + ExFreePoolWithTag( FindBySidCtx->BufferToFree, NC_TAG ); + + } + + FindBySidCtx->BufferToFree = SourceBuffer; + FindBySidCtx->BufferSize = RemnantsSize; + FindBySidCtx->CurrentEntry = InputConsumed; + + SourceBuffer = NULL; + } + + } else { + + // + // We have nothing from the filesystem. Now see what else we need + // to do: + // + // 1. If we have anything left in the buffer, return it now. This + // should be rare, but can happen with multiple async requests + // in different threads on the same handle. If we saw nothing + // in the buffer when this request started (in pre), but there + // is data now, another simultaneous request must have generated + // it. + // + // 2. If this request was sent to the mapping, we are done. Tear + // down mapping state if this is the final outstanding request. + // + // 3. If we need to return results from the mapping, we need to + // set up support for that now. It is important that we return + // all buffered results first, so that name translation is still + // accurate. + // + + FLT_ASSERT( SizeActuallyReturned == 0 ); + SizeWeReturn = 0; + + if (FindBySidCtx->BufferToFree != NULL) { + + // + // We need to clean up any outstanding buffer now. Normally + // this will happen in a pre operation, but we must return some + // data in order for the caller to call us again, and we can't + // set up or tear down the mapping or any existing data in the + // buffer will be returned with wrong names, since the root of + // the operation has changed. + // + + FLT_ASSERT( FindBySidCtx->BufferSize > 0 && + FindBySidCtx->CurrentEntry < FindBySidCtx->BufferSize ); + + // + // Translate the strings in the buffer by applying the parent's + // path, then change the mapping as necessary. + // + + Status = NcFindFilesBySidTranslateBuffers( InstanceContext, + IgnoreCase, + &UserRequestName, + &OpenedName, + Add2Ptr( FindBySidCtx->BufferToFree, FindBySidCtx->CurrentEntry ), + DestBuffer, + FindBySidCtx->BufferSize - FindBySidCtx->CurrentEntry, + BufferSize, + &InputConsumed, + &SizeWeReturn, + (BOOLEAN)UserOverlap.Ancestor ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + FindBySidCtx->CurrentEntry += InputConsumed; + FLT_ASSERT( FindBySidCtx->CurrentEntry <= FindBySidCtx->BufferSize ); + + if (FindBySidCtx->CurrentEntry >= FindBySidCtx->BufferSize) { + + // + // We've finished processing the leftover buffer. Tear it + // down now. Note that we will not attempt to call into + // the filesystem and fully pack the buffer; so long as we + // return something, that's good enough. + // + + ExFreePoolWithTag( FindBySidCtx->BufferToFree, NC_TAG ); + FindBySidCtx->BufferToFree = NULL; + FindBySidCtx->CurrentEntry = 0; + FindBySidCtx->BufferSize = 0; + + } + + if (InputConsumed == 0) { + + FLT_ASSERT( SizeWeReturn == 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + + // + // As an optimization, we could set up the handle here. Doing + // so would save one trip to the filesystem, since the next + // request on this handle will go directly to the mapping. For + // simplicity, we skip this, so the next request will go to the + // filesystem (which will tell us it has nothing to say), and + // we'll perform the create, then retry the request. + // + + goto NcPostFindFilesBySidCleanup; + } + + if (FileObject == FindBySidCtx->RealFileObject) { + + // + // We've completed the request on the user's handle, and we've + // completed the request on the real handle. We are done. + // + // If this is the final outstanding request from the completed + // operation, start cleaning everything up. + // + + if (FindBySidCtx->OutstandingRequests == 1) { + + HANDLE HandleToClose = FindBySidCtx->RealHandle; + PFILE_OBJECT FileObjectToDereference = FindBySidCtx->RealFileObject; + + FindBySidCtx->RealHandle = NULL; + FindBySidCtx->RealFileObject = NULL; + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + FltClose( HandleToClose ); + ObDereferenceObject( FileObjectToDereference ); + } + + } else if (InjectionRequired) { + + OBJECT_ATTRIBUTES MappingAttributes; + HANDLE MappingHandle = NULL; + PFILE_OBJECT MappingFileObject = NULL; + IO_STATUS_BLOCK MappingStatusBlock; + + FLT_ASSERT( FindBySidCtx->RealFileObject == NULL ); + + // + // We have finished enumerating from the filesystem, and we + // need to enumerate from the mapping. At this point, the + // mapping has not been set up yet, so we do that now. + // + // Because we can't hold a lock doing this, we do so + // speculatively: two threads may end up doing this work, and + // one may be thrown away. + // + // Note that if two threads are racing, we should have an + // OutstandingRequests count of 2, so the result cannot be torn + // down by the other thread. + // + + FLT_ASSERT( FindBySidCtx->RealHandle == NULL ); + FLT_ASSERT( FindBySidCtx->OutstandingRequests > 0 ); + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + // + // Open the mapping. We're done returning data on the user's + // handle, but we still need to return data from the mapping. + // + + InitializeObjectAttributes( &MappingAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.FullPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &MappingHandle, // Returned Handle + &MappingFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingAttributes, // object attributes + &MappingStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + if ( Status == STATUS_OBJECT_PATH_NOT_FOUND || + Status == STATUS_OBJECT_NAME_NOT_FOUND ) { + + Status = STATUS_SUCCESS; + } + goto NcPostFindFilesBySidCleanup; + } + + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + + if (FindBySidCtx->RealHandle == NULL) { + + // + // Store our object into the context. + // + + FindBySidCtx->RealHandle = MappingHandle; + FindBySidCtx->RealFileObject = MappingFileObject; + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + } else { + + // + // Another thread beat us to the punch. Tear down + // our state and reload from theirs. + // + + NcUnlockStreamHandleContext( HandleContext ); + UnlockContext = FALSE; + + FltClose( MappingHandle ); + ObDereferenceObject( MappingFileObject ); + } + + MappingHandle = NULL; + MappingFileObject = NULL; + + FileObject = FindBySidCtx->RealFileObject; + + Status = FltFsControlFile( FltObjects->Instance, + FindBySidCtx->RealFileObject, + FSCTL_FIND_FILES_BY_SID, + Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer, + Data->Iopb->Parameters.FileSystemControl.Neither.InputBufferLength, + Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer, + Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength, + &SizeActuallyReturned ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostFindFilesBySidCleanup; + } + + goto NcPostFindFilesBySidMungeBuffer; + + } + } + + Data->IoStatus.Information = SizeWeReturn; + +NcPostFindFilesBySidCleanup: + + if (SourceBuffer != NULL) { + + ExFreePoolWithTag( SourceBuffer, NC_TAG ); + } + + // + // If this function is being called, we were counted as an outstanding + // request. If we made it far enough to have our handle context, we + // can un-count ourselves now. If we failed before that, we remain + // counted, and cleanup occurs when the handle is closed. + // + + FLT_ASSERT( FindBySidCtx != NULL ); + if (FindBySidCtx != NULL) { + FLT_ASSERT( HandleContext != NULL ); + + if (!UnlockContext) { + NcLockStreamHandleContext( HandleContext ); + UnlockContext = TRUE; + } + + FLT_ASSERT( FindBySidCtx->OutstandingRequests > 0 ); + FindBySidCtx->OutstandingRequests--; + } + + if (UnlockContext) { + + FLT_ASSERT( HandleContext != NULL ); + NcUnlockStreamHandleContext( HandleContext ); + } + + if (FileInfo != NULL) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (FileInfoInternalHandle != NULL) { + + FltReleaseFileNameInformation( FileInfoInternalHandle ); + } + + if (HandleContext != NULL) { + + FltReleaseContext( HandleContext ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (!NT_SUCCESS( Status )) { + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + } + + UNREFERENCED_PARAMETER( Flags ); + + return FLT_POSTOP_FINISHED_PROCESSING; + +} + +#if FLT_MGR_WIN7 +FLT_POSTOP_CALLBACK_STATUS +NcPostLookupStreamFromCluster ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + Routine is invoked when the user wants to find a stream owning + a particular cluster. For this call, paths are absolute relative to + the start of the volume. + + Note that this call is system buffered. This means we don't need + to process MDLs, or perform probes, or treat our input data as + suspect. + + All we need to do is walk through the returned data, change names that + refer to the mappings, and return the result. Since the lengths may + change, we may return fewer entries, but will still tell the caller + the number of entries that actually exist and the size of the buffer + to obtain all of them. + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. + + Flags - The flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PLOOKUP_STREAM_FROM_CLUSTER_OUTPUT DestBuffer = Data->Iopb->Parameters.FileSystemControl.Buffered.SystemBuffer; + PLOOKUP_STREAM_FROM_CLUSTER_ENTRY SourceEntry = NULL; + PLOOKUP_STREAM_FROM_CLUSTER_ENTRY DestEntry = NULL; + PLOOKUP_STREAM_FROM_CLUSTER_ENTRY PrevDestEntry = NULL; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NTSTATUS Status; + NC_PATH_OVERLAP Overlap; + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BufferSize = Data->Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength; + ULONG EntryLength; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + PLOOKUP_STREAM_FROM_CLUSTER_OUTPUT SourceBuffer = NULL; + UNICODE_STRING NameString; + UNICODE_STRING Remainder; + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the operation has already failed, we're not interested in attempting + // to process it. + // + + if (!NT_SUCCESS( Data->IoStatus.Status ) || SizeActuallyReturned == 0) { + + Status = Data->IoStatus.Status; + goto NcPostLookupStreamFromClusterCleanup; + } + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostLookupStreamFromClusterCleanup; + } + + if (BufferSize < sizeof( LOOKUP_STREAM_FROM_CLUSTER_OUTPUT )) { + + Status = STATUS_INVALID_PARAMETER; + goto NcPostLookupStreamFromClusterCleanup; + } + + // + // We take a copy of the buffer so that we can re-write the request's + // buffer with our modified data. + // + + SourceBuffer = ExAllocatePoolWithTag( PagedPool, + SizeActuallyReturned, + NC_TAG ); + + if (SourceBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostLookupStreamFromClusterCleanup; + } + + RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned ); + + // + // We initialize this for the first entry. If we don't actually + // copy an entry, it is re-zeroed later. + // + + DestBuffer->Offset = AlignToSize( sizeof( LOOKUP_STREAM_FROM_CLUSTER_OUTPUT ), 8); + DestBuffer->NumberOfMatches = 0; + DestBuffer->BufferSizeRequired = AlignToSize( sizeof( LOOKUP_STREAM_FROM_CLUSTER_OUTPUT ), 8); + + // + // Since this is a buffered FSCTL, we treat the output buffer + // as well formed data. This enables us to make a few assumptions. + // Firstly, we don't expect to walk off the end of the allocation. + // It follows that at least for now, both source and destination + // have the same offsets. + // + + if (SourceBuffer->Offset > 0) { + SourceEntry = Add2Ptr( SourceBuffer, SourceBuffer->Offset ); + DestEntry = Add2Ptr( DestBuffer, SourceBuffer->Offset ); + } + + while (SourceEntry) { + + RtlInitUnicodeString( &NameString, SourceEntry->FileName ); + + // + // Check if the path refers to a location within the real side of + // the mapping. If it does, translate the data to the user side + // of the mapping. + // + + NcComparePath( &NameString, + &InstanceContext->Mapping.RealMapping, + &Remainder, + IgnoreCase, + FALSE, + &Overlap ); + + if (Overlap.InMapping) { + + Status = NcConstructPath( &InstanceContext->Mapping.UserMapping, + &Remainder, + FALSE, + &NameString ); + + if (!NT_SUCCESS( Status )) { + goto NcPostLookupStreamFromClusterCleanup; + } + } + + // + // Note that we're not allowing for NULL, because the structure + // already includes one WCHAR. + // + + EntryLength = AlignToSize( sizeof( LOOKUP_STREAM_FROM_CLUSTER_ENTRY ) + + NameString.Length, 8 ); + + // + // If it fits, copy the data into the returned entry, and advance + // to the next entry to return. + // + + if (DestBuffer->BufferSizeRequired + EntryLength <= BufferSize) { + + DestEntry->Flags = SourceEntry->Flags; + DestEntry->Reserved = SourceEntry->Reserved; + DestEntry->Cluster = SourceEntry->Cluster; + RtlCopyMemory( DestEntry->FileName, + NameString.Buffer, + NameString.Length ); + + DestEntry->FileName[ NameString.Length / sizeof(WCHAR)] = L'\0'; + + DestEntry->OffsetToNext = EntryLength; + + PrevDestEntry = DestEntry; + DestEntry = Add2Ptr( DestEntry, DestEntry->OffsetToNext ); + } + + // + // If we needed to munge the name, we allocated pool to store the + // result. Free that pool now. + // + + if (Overlap.InMapping) { + ExFreePoolWithTag( NameString.Buffer, NC_GENERATE_NAME_TAG ); + } + + // + // We must keep track of the required buffer size and number of + // matches regardless of whether it is returned. + // + + DestBuffer->BufferSizeRequired += EntryLength; + DestBuffer->NumberOfMatches++; + + // + // Advance to the next source entry. We assume our input is + // well formed, and therefore, we do not expect to walk off the + // end of this buffer. + // + + if (SourceEntry->OffsetToNext > 0) { + SourceEntry = Add2Ptr( SourceEntry, SourceEntry->OffsetToNext ); + } else { + SourceEntry = NULL; + } + } + + if (PrevDestEntry) { + PrevDestEntry->OffsetToNext = 0; + } else { + DestBuffer->Offset = 0; + } + + Data->IoStatus.Information = min( DestBuffer->BufferSizeRequired, BufferSize ); + + // + // Since this filter may change the length of names returned, we may + // also change the layout and bytes required. We will not, however, + // change the number of matches; this value refers to the total number + // that exist, not the number that can fit in the buffer. + // + + FLT_ASSERT( DestBuffer->NumberOfMatches == SourceBuffer->NumberOfMatches ); + +NcPostLookupStreamFromClusterCleanup: + + if (SourceBuffer != NULL) { + + ExFreePoolWithTag( SourceBuffer, NC_TAG ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (!NT_SUCCESS( Status )) { + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + } + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + return FLT_POSTOP_FINISHED_PROCESSING; + +} +#endif + +NTSTATUS +NcUsnTranslateBuffers ( + _In_ PNC_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN IgnoreCase, + _In_ ULONGLONG RealMappingParentId, + _In_ ULONGLONG UserMappingParentId, + _In_ PUSN_RECORD InputSystemBuffer, + _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PUSN_RECORD OutputUserBuffer, + _In_ ULONG InputBufferLength, + _In_ ULONG OutputBufferLength, + _Out_ PULONG InputBufferConsumed, + _Out_ PULONG OutputBufferWritten + ) +/*++ + +Routine Description: + + This routine is used to transform buffers from a filesystem view to the + user view. We must replace both the parent ID and link name of any + records referring to the real mapping with corresponding values for the + user mapping. + +Arguments: + + InstanceContext - Pointer to the context describing this instance of the + filter. + + IgnoreCase - TRUE if comparisons should be case insensitive, FALSE if + comparisons should be case sensitive. TODO: Is this required? + + RealMappingParentId - The file ID corresponding to the real mapping + parent. + + UserMappingParentId - The file ID corresponding to the user mapping + parent. + + InputSystemBuffer - The buffer we are processing from. Note that this + routine assumes the buffer is not volatile (cannot be externally + modified.) For this reason, the buffer is expected to be system + buffered by the caller if it is not already. + + Note however that the contents of the buffer may have originated from + a user buffer (via a memcpy), so although the contents are non-volatile, + they are not to be trusted. + + OutputUserBuffer - The buffer we are returning munged results into. + This buffer is expected to have been probed, and this function will + catch and return any invalid buffer exceptions. + + InputBufferLength - Size, in bytes, of the input buffer. + + OutputBufferLength - Size, in bytes, of the output buffer. + + InputBufferConsumed - Pointer to a ULONG which will contain, on output, + the number of bytes processed from the input buffer. This may be + zero, the length of the input buffer, or any value in between. + This value is undefined on failure. + + OutputBufferWritten - Pointer to a ULONG which will contain, on output, + the number of bytes written into the output buffer. This may be + zero, the length of the output buffer, or any value in between. + This value is undefined on failure. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + // + // Pointers into the above buffers used as we read entries from + // one buffer and write them to the other. + // + + PUSN_RECORD SourceEntry = NULL; + PUSN_RECORD DestEntry = NULL; + + // + // Points to a new name if we are required to substitute names for + // this record. If no substitution is required, it is NULL. + // + + PUNICODE_STRING NewLink; + + // + // The length of this record, in bytes. We use this field to refer + // to source and destination records at different times. + // + + ULONG EntryLength; + + ULONG UlongResult; + PVOID PointerResult; + + PAGED_CODE(); + + SourceEntry = InputSystemBuffer; + DestEntry = OutputUserBuffer; + *InputBufferConsumed = 0; + *OutputBufferWritten = 0; + + // + // This routine assumes it will only be called if there is work to do. + // + + FLT_ASSERT( InputBufferLength && OutputBufferLength ); + + try { + + while (SourceEntry) { + + // + // Assume we're not transforming any records, which is the + // common case. + // + + NewLink = NULL; + + // + // If we're about to walk off the end of the buffer, get out + // now. This should only happen if the user has modified the + // buffer after it was returned from the filesystem and before + // we had a chance to copy it. + // + // Firstly check we have enough space for a record, and having + // established that we have enough space to read the first few + // bytes containing the record length, check against that. Use + // safe math routines for the untrusted SourceEntry values. + // + + Status = RtlULongAdd( *InputBufferConsumed, + sizeof(USN_RECORD), + &UlongResult ); + + if (!NT_SUCCESS( Status ) || + (UlongResult > InputBufferLength)) { + + goto FailedBufferCheck; + } + + Status = RtlULongAdd( *InputBufferConsumed, + SourceEntry->RecordLength, + &UlongResult ); + + if (!NT_SUCCESS( Status ) || + (UlongResult > InputBufferLength)) { + + goto FailedBufferCheck; + } + + Status = RtlULongAdd( *InputBufferConsumed, + SourceEntry->FileNameOffset, + &UlongResult ); + + if (NT_SUCCESS( Status )) { + + Status = RtlULongAdd( UlongResult, + SourceEntry->FileNameLength, + &UlongResult ); + } + + if (!NT_SUCCESS( Status ) || + (UlongResult > InputBufferLength) || + (SourceEntry->FileNameOffset < FIELD_OFFSET( USN_RECORD, FileName ))) { + +FailedBufferCheck: + FLT_ASSERT( FALSE ); + SourceEntry = NULL; + Status = STATUS_INVALID_USER_BUFFER; + break; + } + + // + // Assume that the destination record will be the same size as + // the source record. Note that SourceEntry->RecordLength is untrusted, + // so now EntryLength is. + // + + EntryLength = SourceEntry->RecordLength; + + // + // We have encountered a USN record with an incompatible + // version. In theory, MinorVersion changes are compatible, + // MajorVersion changes are not. In practice, a MinorVersion + // change could include offsets to additional data which we have + // no knowledge of, so we can never be sure that we're not + // returning corrupt records on a MinorVersion change. Rather + // than do that, we just give up. + // + + if (SourceEntry->MajorVersion != 2 || + SourceEntry->MinorVersion != 0) { + + SourceEntry = NULL; + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + // + // Check if this is a record we need to transform. If so, adjust + // the length of the destination record appropriately, and record + // which name we should transform to. + // + + if (SourceEntry->ParentFileReferenceNumber == RealMappingParentId && + (SourceEntry->FileNameLength == InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName.Length || + SourceEntry->FileNameLength == InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName.Length)) { + + UNICODE_STRING TempString; + + TempString.Buffer = Add2Ptr( SourceEntry, SourceEntry->FileNameOffset ); + TempString.Length = TempString.MaximumLength = SourceEntry->FileNameLength; + + if (RtlCompareUnicodeString( &TempString, + &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName, + IgnoreCase ) == 0) { + + NewLink = &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName; + + } else + if (RtlCompareUnicodeString( &TempString, + &InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName, + IgnoreCase ) == 0) { + + NewLink = &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName; + + } + + if (NewLink != NULL) { + + EntryLength = AlignToSize( SourceEntry->FileNameOffset + NewLink->Length, 8 ); + } + } + + // + // If the current entry would overflow the remaining output buffer + // then we've done all we can. Return now to let our caller deal + // with the remaining buffer. + // + + Status = RtlULongAdd( EntryLength, + *OutputBufferWritten, + &UlongResult ); + + if (!NT_SUCCESS( Status ) || + (UlongResult > OutputBufferLength)) { + + break; + } + + // + // If we're not transforming (the common case) copy the entire + // record blindly from source to destination. If we are + // transforming, copy the record, switch the file ID, then + // copy the name. + // + + if (NewLink == NULL) { + + RtlCopyMemory( DestEntry, + SourceEntry, + EntryLength ); + + } else { + + + // + // Copy all of the record up to the file name offset. This + // is the most resilient approach to a minor version change + // which adds new fields. We will preserve the minor + // version number, and preserve the fields in this process. + // Note that this is a moot exercise here, since we are + // failing on unknown minor versions above. + // + + RtlCopyMemory( DestEntry, + SourceEntry, + SourceEntry->FileNameOffset ); + + DestEntry->ParentFileReferenceNumber = UserMappingParentId; + + DestEntry->FileNameLength = NewLink->Length; + DestEntry->FileNameOffset = FIELD_OFFSET( USN_RECORD, FileName ); + DestEntry->RecordLength = EntryLength; + + // + // Since the DestBuffer is user-exposed, we cannot trust + // its contents immediately after writing them. Here, we + // calculate the file offset from the source entry because + // that is system buffered. + // + + RtlCopyMemory( Add2Ptr( DestEntry, SourceEntry->FileNameOffset ), + NewLink->Buffer, + NewLink->Length ); + } + + Status = RtlULongAdd( *InputBufferConsumed, + SourceEntry->RecordLength, + InputBufferConsumed ); + + PointerResult = Add2Ptr( SourceEntry, SourceEntry->RecordLength ); + + // + // There's a problem if one of the following happened: + // + // 1) We overflowed when accounting for for consumed input buffer + // 2) We wrapped when advancing SourceEntry + // 3) PointerResult is not within InputSystemBuffer + // + + if (!NT_SUCCESS( Status ) || + (PointerResult < (PVOID)SourceEntry) || + (PointerResult < Add2Ptr( InputSystemBuffer, sizeof(USN_RECORD) ))) { + + FLT_ASSERT( FALSE ); + + Status = STATUS_INVALID_USER_BUFFER; + break; + } + + // + // We verified earlier that adding EntryLength to *OutputBufferWritten + // would not overflow our output buffer, meaning advancing DestEntry + // by EntryLength bytes is also safe. + // + + *OutputBufferWritten += EntryLength; + + DestEntry = Add2Ptr( DestEntry, EntryLength ); + + SourceEntry = (PUSN_RECORD)PointerResult; + + // + // If we've just advanced our next location beyond the end of the + // input buffer, or there isn't enough room in it for even a USN_RECORD + // structure, terminate the loop by setting SourceEntry to NULL so + // we can at least return the valid entries we have. + // + + FLT_ASSERT( *InputBufferConsumed <= InputBufferLength ); + + if ((*InputBufferConsumed >= InputBufferLength) || + (*InputBufferConsumed + FIELD_OFFSET( USN_RECORD, FileName ) > InputBufferLength)) { + + // + // Indicate to the caller that we consumed exactly the input buffer. + // Otherwise it may hold the possibly small remnant and come back + // in to this routine later with that remnant, causing us to overread + // the buffer. + // + + *InputBufferConsumed = InputBufferLength; + SourceEntry = NULL; + } + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + return Status; +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostReadFileUsnData ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the file object we are enumerating + under for find files by SID. + + Flags - The flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NTSTATUS Status; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + PFLT_FILE_NAME_INFORMATION FileInfo = NULL; + + // + // The user's buffer that we will be writing to, and the system copy + // of the data returned by the file system which we will read from. + // + + PUSN_RECORD DestBuffer = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer; + PUSN_RECORD SourceBuffer = NULL; + + // + // The opened path's relation to the mappings. + // + + NC_PATH_OVERLAP UserOverlap; + + // + // Size returned from the filesystem to us, size of the user's buffer + // that we can legitimately write to, the amount we actually wrote into + // the user's buffer, and the amount of the filesystem's buffer we have + // processed so far. + // + + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BufferSize = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength; + ULONG InputConsumed; + ULONG SizeWeReturn; + + // + // File IDs corresponding to the parent of the user mapping, and + // parent of the real mapping. + // + + ULONGLONG RealMappingParentId; + ULONGLONG UserMappingParentId; + + OBJECT_ATTRIBUTES MappingParentAttributes; + HANDLE MappingParentHandle = NULL; + PFILE_OBJECT MappingParentFileObject = NULL; + IO_STATUS_BLOCK MappingParentStatusBlock; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the operation has already failed, we're not interested in attempting + // to process it. + // + + if (!NT_SUCCESS( Data->IoStatus.Status )) { + + Status = Data->IoStatus.Status; + goto NcPostReadFileUsnDataCleanup; + } + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + FLT_ASSERT( BufferSize >= SizeActuallyReturned ); + if (BufferSize < sizeof( USN_RECORD ) || + SizeActuallyReturned < sizeof( USN_RECORD )) { + + Status = STATUS_BUFFER_TOO_SMALL; + goto NcPostReadFileUsnDataCleanup; + } + + // + // Get the file handle's name. We are only interested in + // processing calls destined to the mapping. + // + + Status = NcGetFileNameInformation( Data, + NULL, + NULL, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT | + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, + &FileInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + Status = FltParseFileNameInformation( FileInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + NcComparePath( &FileInfo->Name, + &InstanceContext->Mapping.UserMapping, + NULL, + IgnoreCase, + TRUE, + &UserOverlap ); + + if (!UserOverlap.Match) { + + Status = STATUS_SUCCESS; + goto NcPostReadFileUsnDataCleanup; + } + + // + // If another driver has posted the request, we may need to map a + // system buffer for the call from its Mdl. If not, we can use the + // user address passed in to the call. Be sure to check the "user" + // address really does point to user memory. + // + + if (Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress != NULL) { + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (DestBuffer == NULL) { + + Status = STATUS_NO_MEMORY; + goto NcPostReadFileUsnDataCleanup; + } + + } else { + + try { + + if (Data->RequestorMode != KernelMode) { + ProbeForWrite( DestBuffer, BufferSize, sizeof( UCHAR )); + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + } + + // + // Open the mapping parents and query IDs. + // + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostReadFileUsnDataCleanup; + } + + Status = FltQueryInformationFile( FltObjects->Instance, + MappingParentFileObject, + &RealMappingParentId, + sizeof(RealMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.UserMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostReadFileUsnDataCleanup; + } + + Status = FltQueryInformationFile( FltObjects->Instance, + MappingParentFileObject, + &UserMappingParentId, + sizeof(UserMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + // + // Allocate a new buffer and copy the contents. Note that this is + // particularly important with this call, since it's not system + // buffered; the contents are free to change underneath us. This + // allocation protects us against that, but we still must be + // paranoid touching the buffer, since we cannot trust that it has + // any integrity at this point. + // + + SourceBuffer = ExAllocatePoolWithTag( PagedPool, + SizeActuallyReturned, + NC_TAG ); + + if (SourceBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostReadFileUsnDataCleanup; + } + + try { + + RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + goto NcPostReadFileUsnDataCleanup; + } + + Status = NcUsnTranslateBuffers( InstanceContext, + IgnoreCase, + RealMappingParentId, + UserMappingParentId, + SourceBuffer, + DestBuffer, + SizeActuallyReturned, + BufferSize, + &InputConsumed, + &SizeWeReturn ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadFileUsnDataCleanup; + } + + if (InputConsumed == 0) { + FLT_ASSERT( SizeWeReturn == 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + + FLT_ASSERT( InputConsumed == SizeActuallyReturned ); + + Data->IoStatus.Information = SizeWeReturn; + +NcPostReadFileUsnDataCleanup: + + if (SourceBuffer != NULL) { + + ExFreePoolWithTag( SourceBuffer, NC_TAG ); + } + + if (FileInfo != NULL) { + + FltReleaseFileNameInformation( FileInfo ); + } + + if (MappingParentHandle != NULL) { + + FltClose( MappingParentHandle ); + } + + if (MappingParentFileObject != NULL) { + + ObDereferenceObject( MappingParentFileObject ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (!NT_SUCCESS( Status )) { + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + } + + UNREFERENCED_PARAMETER( Flags ); + + return FLT_POSTOP_FINISHED_PROCESSING; + +} + + +FLT_POSTOP_CALLBACK_STATUS +NcPostEnumUsnData ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the file object we are enumerating + under for find files by SID. + + Flags - The flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NTSTATUS Status; + BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + // + // The user's buffer that we will be writing to, and the system copy + // of the data returned by the file system which we will read from. + // + + PUSN_RECORD DestBuffer = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer; + PUSN_RECORD SourceBuffer = NULL; + + // + // Size returned from the filesystem to us, size of the user's buffer + // that we can legitimately write to, the amount we actually wrote into + // the user's buffer, and the amount of the filesystem's buffer we have + // processed so far. + // + + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BufferSize = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength; + ULONG InputConsumed; + ULONG SizeWeReturn; + + // + // File IDs corresponding to the parent of the user mapping, and + // parent of the real mapping. + // + + ULONGLONG RealMappingParentId; + ULONGLONG UserMappingParentId; + + OBJECT_ATTRIBUTES MappingParentAttributes; + HANDLE MappingParentHandle = NULL; + PFILE_OBJECT MappingParentFileObject = NULL; + IO_STATUS_BLOCK MappingParentStatusBlock; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the operation has already failed, we're not interested in attempting + // to process it. + // + + if (!NT_SUCCESS( Data->IoStatus.Status )) { + + Status = Data->IoStatus.Status; + goto NcPostEnumUsnDataCleanup; + } + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostEnumUsnDataCleanup; + } + + FLT_ASSERT( BufferSize >= SizeActuallyReturned ); + if (BufferSize < sizeof( USN_RECORD ) || + SizeActuallyReturned < sizeof( USN_RECORD )) { + + Status = STATUS_BUFFER_TOO_SMALL; + goto NcPostEnumUsnDataCleanup; + } + + // + // If another driver has posted the request, we may need to map a + // system buffer for the call from its Mdl. If not, we can use the + // user address passed in to the call. Be sure to check the "user" + // address really does point to user memory. + // + + if (Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress != NULL) { + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostEnumUsnDataCleanup; + } + + DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (DestBuffer == NULL) { + + Status = STATUS_NO_MEMORY; + goto NcPostEnumUsnDataCleanup; + } + + } else { + + try { + + if (Data->RequestorMode != KernelMode) { + ProbeForWrite( DestBuffer, BufferSize, sizeof( UCHAR )); + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS( Status )) { + + goto NcPostEnumUsnDataCleanup; + } + } + + // + // Open the mapping parents and query IDs. + // + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostEnumUsnDataCleanup; + } + + Status = FltQueryInformationFile( FltObjects->Instance, + MappingParentFileObject, + &RealMappingParentId, + sizeof(RealMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostEnumUsnDataCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.UserMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + FltObjects->Instance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + FltObjects->FileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostEnumUsnDataCleanup; + } + + Status = FltQueryInformationFile( FltObjects->Instance, + MappingParentFileObject, + &UserMappingParentId, + sizeof(UserMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostEnumUsnDataCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + // + // Allocate a new buffer and copy the contents. Note that this is + // particularly important with this call, since it's not system + // buffered; the contents are free to change underneath us. This + // allocation protects us against that, but we still must be + // paranoid touching the buffer, since we cannot trust that it has + // any integrity at this point. + // + + SourceBuffer = ExAllocatePoolWithTag( PagedPool, + SizeActuallyReturned, + NC_TAG ); + + if (SourceBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostEnumUsnDataCleanup; + } + + try { + + RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + goto NcPostEnumUsnDataCleanup; + } + + Status = NcUsnTranslateBuffers( InstanceContext, + IgnoreCase, + RealMappingParentId, + UserMappingParentId, + Add2Ptr( SourceBuffer, sizeof(USN) ), + Add2Ptr( DestBuffer, sizeof(USN) ), + SizeActuallyReturned - sizeof(USN), + BufferSize - sizeof(USN), + &InputConsumed, + &SizeWeReturn ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostEnumUsnDataCleanup; + } + + if (InputConsumed == 0) { + FLT_ASSERT( SizeWeReturn == 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + + FLT_ASSERT( InputConsumed == SizeActuallyReturned - sizeof( USN )); + + Data->IoStatus.Information = SizeWeReturn + sizeof(USN); + +NcPostEnumUsnDataCleanup: + + if (SourceBuffer != NULL) { + + ExFreePoolWithTag( SourceBuffer, NC_TAG ); + } + + if (MappingParentHandle != NULL) { + + FltClose( MappingParentHandle ); + } + + if (MappingParentFileObject != NULL) { + + ObDereferenceObject( MappingParentFileObject ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (!NT_SUCCESS( Status )) { + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + } + + UNREFERENCED_PARAMETER( Flags ); + + return FLT_POSTOP_FINISHED_PROCESSING; + +} + +VOID +NcPostReadUsnJournalWorker ( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID Context + ) +/*++ + +Routine Description: + + +Arguments: + + Data - Pointer to the filter CallbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure + containing opaque handles to this filter, instance, its + associated volume and file object. + + CompletionContext - The context for the completion routine for + this operation. We set this to the file object we are enumerating + under for find files by SID. + + Flags - The flags for this operation. + +Return Value: + + The return value is the Status of the operation. + +--*/ +{ + PFLT_CALLBACK_DATA Data = (PFLT_CALLBACK_DATA)Context; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NTSTATUS Status; + BOOLEAN IgnoreCase = !BooleanFlagOn( Data->Iopb->TargetFileObject->Flags, + FO_OPENED_CASE_SENSITIVE ); + + // + // The user's buffer that we will be writing to, and the system copy + // of the data returned by the file system which we will read from. + // + + PUSN_RECORD DestBuffer = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer; + PUSN_RECORD SourceBuffer = NULL; + + // + // Size returned from the filesystem to us, size of the user's buffer + // that we can legitimately write to, the amount we actually wrote into + // the user's buffer, and the amount of the filesystem's buffer we have + // processed so far. + // + + ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information; + ULONG BufferSize = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength; + ULONG InputConsumed; + ULONG SizeWeReturn; + + // + // File IDs corresponding to the parent of the user mapping, and + // parent of the real mapping. + // + + ULONGLONG RealMappingParentId; + ULONGLONG UserMappingParentId; + + OBJECT_ATTRIBUTES MappingParentAttributes; + HANDLE MappingParentHandle = NULL; + PFILE_OBJECT MappingParentFileObject = NULL; + IO_STATUS_BLOCK MappingParentStatusBlock; + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // If the operation has already failed, we're not interested in attempting + // to process it. + // + + if (!NT_SUCCESS( Data->IoStatus.Status )) { + + Status = Data->IoStatus.Status; + goto NcPostReadUsnJournalSafeCleanup; + } + + Status = FltGetInstanceContext( Data->Iopb->TargetInstance, + &InstanceContext); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadUsnJournalSafeCleanup; + } + + FLT_ASSERT( BufferSize >= SizeActuallyReturned ); + if (BufferSize < sizeof( USN_RECORD ) + sizeof( USN )|| + SizeActuallyReturned < sizeof( USN_RECORD ) + sizeof( USN )) { + + Status = STATUS_BUFFER_TOO_SMALL; + goto NcPostReadUsnJournalSafeCleanup; + } + + // + // We always post this request. In future, we may skip this post + // in some conditions, but for now, OutputMdlAddress had better + // be valid. + // + + FLT_ASSERT( Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress != NULL ); + if (Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress != NULL) { + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadUsnJournalSafeCleanup; + } + + DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (DestBuffer == NULL) { + + Status = STATUS_NO_MEMORY; + goto NcPostReadUsnJournalSafeCleanup; + } + + } else { + + try { + + if (Data->RequestorMode != KernelMode) { + ProbeForWrite( DestBuffer, BufferSize, sizeof( UCHAR )); + } + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadUsnJournalSafeCleanup; + } + } + + // + // Open the mapping parents and query IDs. + // + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + Data->Iopb->TargetInstance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + Data->Iopb->TargetFileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostReadUsnJournalSafeCleanup; + } + + Status = FltQueryInformationFile( Data->Iopb->TargetInstance, + MappingParentFileObject, + &RealMappingParentId, + sizeof(RealMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadUsnJournalSafeCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + InitializeObjectAttributes( &MappingParentAttributes, + &InstanceContext->Mapping.UserMapping.LongNamePath.ParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter + Data->Iopb->TargetInstance, // Instance + &MappingParentHandle, // Returned Handle + &MappingParentFileObject, // Returned FileObject + FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access + &MappingParentAttributes, // object attributes + &MappingParentStatusBlock, // Returned IOStatusBlock + 0, // Allocation Size + FILE_ATTRIBUTE_NORMAL, // File Attributes + 0, // Share Access + FILE_OPEN, // Create Disposition + FILE_DIRECTORY_FILE, // Create Options + NULL, // Ea Buffer + 0, // EA Length + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + Data->Iopb->TargetFileObject ); // Transaction info. + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND ); + + goto NcPostReadUsnJournalSafeCleanup; + } + + Status = FltQueryInformationFile( Data->Iopb->TargetInstance, + MappingParentFileObject, + &UserMappingParentId, + sizeof(UserMappingParentId), + FileInternalInformation, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadUsnJournalSafeCleanup; + } + + FltClose( MappingParentHandle ); + ObDereferenceObject( MappingParentFileObject ); + + MappingParentHandle = NULL; + MappingParentFileObject = NULL; + + // + // Allocate a new buffer and copy the contents. Note that this is + // particularly important with this call, since it's not system + // buffered; the contents are free to change underneath us. This + // allocation protects us against that, but we still must be + // paranoid touching the buffer, since we cannot trust that it has + // any integrity at this point. + // + + SourceBuffer = ExAllocatePoolWithTag( PagedPool, + SizeActuallyReturned, + NC_TAG ); + + if (SourceBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcPostReadUsnJournalSafeCleanup; + } + + try { + + RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned ); + + } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { + + Status = STATUS_INVALID_USER_BUFFER; + goto NcPostReadUsnJournalSafeCleanup; + } + + Status = NcUsnTranslateBuffers( InstanceContext, + IgnoreCase, + RealMappingParentId, + UserMappingParentId, + Add2Ptr( SourceBuffer, sizeof(USN) ), + Add2Ptr( DestBuffer, sizeof(USN) ), + SizeActuallyReturned - sizeof(USN), + BufferSize - sizeof(USN), + &InputConsumed, + &SizeWeReturn ); + + if (!NT_SUCCESS( Status )) { + + goto NcPostReadUsnJournalSafeCleanup; + } + + if (InputConsumed == 0) { + FLT_ASSERT( SizeWeReturn == 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + + FLT_ASSERT( InputConsumed == SizeActuallyReturned - sizeof( USN )); + + Data->IoStatus.Information = SizeWeReturn + sizeof(USN); + +NcPostReadUsnJournalSafeCleanup: + + if (SourceBuffer != NULL) { + + ExFreePoolWithTag( SourceBuffer, NC_TAG ); + } + + if (MappingParentHandle != NULL) { + + FltClose( MappingParentHandle ); + } + + if (MappingParentFileObject != NULL) { + + ObDereferenceObject( MappingParentFileObject ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (!NT_SUCCESS( Status )) { + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + } + + FltCompletePendedPostOperation( Data ); + + FltFreeGenericWorkItem( WorkItem ); + + UNREFERENCED_PARAMETER( Filter ); + +} + +FLT_POSTOP_CALLBACK_STATUS +NcPostReadUsnJournal ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + NTSTATUS Status; + PFLT_GENERIC_WORKITEM WorkItem; + + // + // Lock the user's buffer so we can post this request. + // + + Status = FltLockUserBuffer( Data ); + if (!NT_SUCCESS( Status )) { + + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Allocate a workitem and post. At this point, we will typically + // have TopLevelIrp set (along with filesystem locks.) We don't want + // to issue creates back into the filesystem from here. Note that + // we can get away with this because the filesystem will not wait - + // it has completed this request, and will go on from here. + // + + WorkItem = FltAllocateGenericWorkItem(); + + if (WorkItem == NULL) { + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + return FLT_POSTOP_FINISHED_PROCESSING; + } + + Status = FltQueueGenericWorkItem( WorkItem, + Data->Iopb->TargetInstance, + NcPostReadUsnJournalWorker, + CriticalWorkQueue, + Data ); + + if (!NT_SUCCESS( Status )) { + + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; + return FLT_POSTOP_FINISHED_PROCESSING; + } + + return FLT_POSTOP_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); +} + diff --git a/filesys/miniFilter/NameChanger/nchelper.c b/filesys/miniFilter/NameChanger/nchelper.c new file mode 100644 index 00000000..72443de4 --- /dev/null +++ b/filesys/miniFilter/NameChanger/nchelper.c @@ -0,0 +1,365 @@ +/*++ + +Copyright (c) 2008 - 2009 Microsoft Corporation + +Module Name: + + nchelper.c + +Abstract: + + Contains helper routines of general applicability across name changer. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcAllocateEResource) +#pragma alloc_text(PAGE, NcCreateFileHelper) +#pragma alloc_text(PAGE, NcGetFileNameInformation) +#endif + +_When_(Data == NULL, _Pre_satisfies_(FileObject != NULL && Instance != NULL)) +_When_(FileObject == NULL || Instance == NULL, _Pre_satisfies_(Data != NULL)) +NTSTATUS +NcGetFileNameInformation( + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_opt_ PFILE_OBJECT FileObject, + _In_opt_ PFLT_INSTANCE Instance, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Outptr_ PFLT_FILE_NAME_INFORMATION *FileNameInformation + ) +/*++ + +Routine Description: + + This function is a wrapper to call the correct variant of + FltGetFileNameInformation depending on the information we happen to + have available. + +Arguments: + + Data - Pointer to the callback data structure associated with a request. + This is optional, but if not specified, FileObject and Instance must + be supplied. + + FileObject - Pointer to the file object to query a name on. Optional, + but if not supplied, Data must be supplied. + + Instance - Pointer to the instance of our filter to query the name on. + Optional, but if not supplied, Data must be supplied. + + NameOptions - FLT_FILE_NAME_* flags for this request. + + FileNameInformation - On output, contains the file name information + resulting from this query. On failure, contents are undefined. + On success, caller is responsible for releasing this with + FltReleaseFileNameInformation. + +Return Value: + + Returns the status of the operation. + +--*/ +{ + NTSTATUS Status; + + PAGED_CODE(); + + FLT_ASSERT( Data || FileObject ); + + *FileNameInformation = NULL; + + if (ARGUMENT_PRESENT( Data )) { + + Status = FltGetFileNameInformation( Data, + NameOptions, + FileNameInformation ); + + } else if (ARGUMENT_PRESENT( FileObject )) { + + Status = FltGetFileNameInformationUnsafe( FileObject, + Instance, + NameOptions, + FileNameInformation ); + // + // This should never happen, as either Data or FileObject must be non-NULL. + // + + } else { + + FLT_ASSERT( FALSE ); + Status = STATUS_INVALID_PARAMETER; + } + + return Status; +} + +NTSTATUS +NcAllocateEResource( + _Out_ PERESOURCE * OutputLock + ) +/*++ + +Routine Description: + + This function allocates a new ERESOURCE. + +Arguments: + + OutputLock - On output, points to a newly allocated ERESOURCE. This + value is undefined on failure. Caller is responsible for deleting + this with a call to NcFreeEResource. + +Return Value: + + Returns the status of the operation. + +--*/ +{ + NTSTATUS Status; + + PERESOURCE Lock = NULL; + BOOLEAN DeleteLock = FALSE; + + PAGED_CODE(); + + Lock = ExAllocatePoolWithTag( NonPagedPool, + sizeof(ERESOURCE), + NC_LOCK_TAG ); + + if (Lock == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcAllocateEResourceCleanup; + } + + Status = ExInitializeResourceLite( Lock ); + + if (!NT_SUCCESS( Status )) { + + goto NcAllocateEResourceCleanup; + } + + Status = STATUS_SUCCESS; + DeleteLock = TRUE; + *OutputLock = Lock; + +NcAllocateEResourceCleanup: + + if (!NT_SUCCESS( Status )) { + + if (Lock) { + + if (DeleteLock) { + + ExDeleteResourceLite( Lock ); + } + ExFreePoolWithTag( Lock, NC_LOCK_TAG ); + } + } + return Status; +} + +VOID +NcFreeEResource( + _In_ PERESOURCE Lock + ) +/*++ + +Routine Description: + + This function frees an ERESOURCE. + +Arguments: + + Lock - Points to an ERESOURCE allocated previously via + NcAllocateEResource that needs to be torn down. + +Return Value: + + Returns the status of the operation. + +--*/ +{ + ExDeleteResourceLite( Lock ); + ExFreePoolWithTag( Lock, NC_LOCK_TAG ); +} + +NTSTATUS +NcCreateFileHelper ( + _In_ PFLT_FILTER Filter, + _In_opt_ PFLT_INSTANCE Instance, + _Out_ PHANDLE FileHandle, + _Outptr_opt_ PFILE_OBJECT *FileObject, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, + _In_ ULONG EaLength, + _In_ ULONG Flags, + _In_opt_ PFILE_OBJECT ParentFileObject + ) +{ + +#if FLT_MGR_LONGHORN + IO_DRIVER_CREATE_CONTEXT DriverContext; +#endif + NTSTATUS Status; + + PAGED_CODE(); + +#if FLT_MGR_LONGHORN + IoInitializeDriverCreateContext( &DriverContext ); + + if (ARGUMENT_PRESENT( ParentFileObject )) { + PTXN_PARAMETER_BLOCK TxnInfo; + + TxnInfo = IoGetTransactionParameterBlock( ParentFileObject ); + + // + // If we have a FileObject, and if it has a transaction, pass the + // same transaction context along to our internal creates. + // + // TxfInfo will be valid so long as the file object is valid, which + // it is assumed to be across the call to this routine. + // + + DriverContext.TxnParameters = TxnInfo; + } +#else + UNREFERENCED_PARAMETER( ParentFileObject ); +#endif + + Status = NcCreateFileEx2( Filter, + Instance, + FileHandle, + FileObject, + DesiredAccess, + ObjectAttributes, + IoStatusBlock, + AllocationSize, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + EaBuffer, + EaLength, + Flags, +#if FLT_MGR_LONGHORN + &DriverContext ); +#else + NULL ); +#endif + + return Status; +} + + +NTSTATUS +NcSetCancelCompletion( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PFLT_COMPLETE_CANCELED_CALLBACK CanceledCallback + ) +/*++ + +Routine Description: + + This sets the cancel routine attached to the request. We + synchronize against the request being cancelled via the system + global cancel spinlock, and if the request is already cancelled, + this function returns STATUS_CANCELLED to the caller to + indicate that the caller must clean up the request. Otherwise, + the cancel routine is set and immediately upon return any + cancellation will be via that mechanism. + + NOTE: This routine must be nonpaged, since we require + synchronization with the system global cancel spinlock. + +Arguments: + + Data - Pointer to the request we wish to assign cancellation to. + + CanceledCallback - Pointer to the functin to call upon + cancellation. + +Return Value: + + STATUS_CANCELLED if the request should be completed by the + caller, STATUS_SUCCESS to indicate that the cancellation + routine is associated with the request. + +--*/ +{ + NTSTATUS Status; + KIRQL OldIrql; + + IoAcquireCancelSpinLock( &OldIrql ); + + if (FltIsIoCanceled( Data )) { + IoReleaseCancelSpinLock( OldIrql ); + return STATUS_CANCELLED; + + } + Status = FltSetCancelCompletion( Data, CanceledCallback ); + + IoReleaseCancelSpinLock( OldIrql ); + return Status; +} + + +LONG +NcExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ) +/*++ + +Routine Description: + + Exception filter to catch errors touching user buffers. + +Arguments: + + ExceptionPointer - The exception record. + + AccessingUserBuffer - If TRUE, overrides FsRtlIsNtStatusExpected to allow + the caller to munge the error to a desired status. + +Return Value: + + EXCEPTION_EXECUTE_HANDLER - If the exception handler should be run. + + EXCEPTION_CONTINUE_SEARCH - If a higher exception handler should take care of + this exception. + +--*/ +{ + NTSTATUS Status; + + Status = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // Certain exceptions shouldn't be dismissed within the namechanger filter + // unless we're touching user memory. + // + + if (!FsRtlIsNtstatusExpected( Status ) && + !AccessingUserBuffer) { + + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; +} + diff --git a/filesys/miniFilter/NameChanger/ncinit.c b/filesys/miniFilter/NameChanger/ncinit.c new file mode 100644 index 00000000..b61f76d9 --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncinit.c @@ -0,0 +1,491 @@ + +#include "nc.h" + +_At_(OutputString->Buffer, _Post_notnull_) +NTSTATUS +NcLoadRegistryString ( + _In_ HANDLE Key, + _In_ PCWSTR valueName, + _Out_ PUNICODE_STRING OutputString + ); + +BOOLEAN +NcIs8DOT3Compatible ( + _In_ PUNICODE_STRING TestName, + _In_opt_ PUNICODE_STRING LongName + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, NcInitializeMapping) +#pragma alloc_text(INIT, NcLoadRegistryString) +#pragma alloc_text(INIT, NcIs8DOT3Compatible) +#endif + +// The #pragma is a notation to the static code analyzer that the Buffer +// returned from the function will always be properly initialized. +// The multiple allocations/frees of the buffers causes it to lose track. +#pragma warning(push) +#pragma warning(disable:6001) +_At_(OutputString->Buffer, _Post_notnull_) +NTSTATUS +NcLoadRegistryString ( + _In_ HANDLE Key, + _In_ PCWSTR valueName, + _Out_ PUNICODE_STRING OutputString + ) +{ +#pragma warning(pop) + PKEY_VALUE_PARTIAL_INFORMATION TempMappingBuffer = NULL; + ULONG TempMappingKeyLength = 0; + UNICODE_STRING ValueString; + PWCHAR OutputStringBuffer = NULL; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Query the length of the registry value. + // + + RtlInitUnicodeString( &ValueString, valueName ); + +NcLoadRegistryStringRetry: + Status = ZwQueryValueKey( Key, + &ValueString, + KeyValuePartialInformation, + NULL, + 0, + &TempMappingKeyLength ); + + // + // If we could not successfully locate the value, return the + // error to our caller. + // + + if (Status != STATUS_BUFFER_TOO_SMALL && + Status != STATUS_BUFFER_OVERFLOW) { + + goto NcLoadRegistryStringCleanup; + } + + // + // Allocate a buffer large enough to hold the string. + // + + if (TempMappingBuffer != NULL) { + ExFreePoolWithTag( TempMappingBuffer, NC_TAG ); + } + + TempMappingBuffer = ExAllocatePoolWithTag( PagedPool, + TempMappingKeyLength, + NC_TAG ); + + if (TempMappingBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcLoadRegistryStringCleanup; + } + + // + // Now attempt to read the string. + // + + Status = ZwQueryValueKey( Key, + &ValueString, + KeyValuePartialInformation, + TempMappingBuffer, + TempMappingKeyLength, + &TempMappingKeyLength ); + + // + // If the value is changing underneath us, the length we + // collected above may be stale. Loop back, reallocate + // and try again. + // + + if (Status == STATUS_BUFFER_TOO_SMALL || + Status == STATUS_BUFFER_OVERFLOW) { + + goto NcLoadRegistryStringRetry; + } + + if (!NT_SUCCESS( Status )) { + + goto NcLoadRegistryStringCleanup; + } + + // + // If we're reading a string, it had better: + // 1. Be a string. + // 2. Fit in a UNICODE_STRING. + // 3. Have some characters in it (we never need empty strings in this filter.) + // + + if (TempMappingBuffer->Type != REG_SZ || + TempMappingBuffer->DataLength >= MAXUSHORT || + TempMappingBuffer->DataLength <= sizeof(WCHAR)) { + + Status = STATUS_INVALID_PARAMETER; + goto NcLoadRegistryStringCleanup; + } + + // + // Allocate a buffer for the target string. Note that we + // allocate one fewer WCHAR, as we have no need for the + // NULL terminator in our UNICODE_STRING. + // + + OutputStringBuffer = ExAllocatePoolWithTag( NonPagedPool, + TempMappingBuffer->DataLength - sizeof(WCHAR), + NC_TAG ); + + if (OutputStringBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcLoadRegistryStringCleanup; + } + + // + // We only modify the output string on success. On failure, it is + // left with previous values. + // + + Status = STATUS_SUCCESS; + + OutputString->MaximumLength = (USHORT)TempMappingBuffer->DataLength - sizeof(WCHAR); + OutputString->Buffer = OutputStringBuffer; + + RtlCopyMemory( OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength ); + OutputString->Length = OutputString->MaximumLength; + + // + // This buffer is in use and should not be cleaned up. + // + + OutputStringBuffer = NULL; + +NcLoadRegistryStringCleanup: + + if (TempMappingBuffer != NULL) { + ExFreePoolWithTag( TempMappingBuffer, NC_TAG ); + } + + if (OutputStringBuffer != NULL) { + ExFreePoolWithTag( OutputStringBuffer, NC_TAG ); + } + + return Status; +} + +BOOLEAN +NcIs8DOT3Compatible ( + _In_ PUNICODE_STRING TestName, + _In_opt_ PUNICODE_STRING LongName + ) +{ + BOOLEAN SpacesPresent; + USHORT Index; + PAGED_CODE(); + + // + // When the user supplies a shortname, we expect it to be a valid + // shortname. This function will check the name's length. + // + + if (!RtlIsNameLegalDOS8Dot3( TestName, + NULL, + &SpacesPresent )) { + + return FALSE; + + } + + // + // Our shortnames should not have spaces. + // + + if (SpacesPresent) { + return FALSE; + } + + // + // In this sample, we enforce that the shortname must NOT contain + // a tilde (~). + // + // If the shortname could contain a tilde, the filesystem would + // be able to autogenerate a conflicting shortname in response to + // an operation on a long name. We could only detect this + // afterwards (in a post-operation callback), but we may not be + // able to handle the condition correctly. Explicitly changing + // a shortname requires NTFS and restore privilege. Rather than + // attempt to obtain this functionality via creative mechanism, + // this filter simply prevents a shortname which could create + // this condition. + // + // Note that in a product it may be advantageous to create both + // sides of the mapping on disk. Part of this sample is to + // illustrate the emulation of an object which does not exist, + // so we did not do this here. + // + // We enforce: + // 1. The name must not have a tilde (for the above reasons); + // 2. The name must not contain a path seperator; + // 3. The name must be fully uppercase to be a valid DOS name + // + + for (Index = 0; + Index < TestName->Length/sizeof(WCHAR); + Index++) { + + if (TestName->Buffer[Index] == L'~' || + TestName->Buffer[Index] == L'\\' || + TestName->Buffer[Index] != RtlUpcaseUnicodeChar( TestName->Buffer[Index] )) { + + return FALSE; + + } + } + + // + // We're done validating the short name. Now we must check the + // long and short names for consistency. If we have no long name, + // we're done. + // + + if (!ARGUMENT_PRESENT( LongName )) { + return TRUE; + } + + // + // Check if the long name is a valid shortname. We recurse into + // ourselves for this. Note that since we're not specifying a + // long name, the recursion is bounded. + // + + if (NcIs8DOT3Compatible( LongName, NULL )) { + + // + // If both our long and short paths are compliant, they had + // better be the same. + // + + if (!RtlEqualUnicodeString( TestName, LongName, FALSE )) { + return FALSE; + } + } + + return TRUE; + +} + +NTSTATUS +NcInitializeMapping( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine initializes the mapping structure. It will + try to populate it from the registry, and if that fails + use a default string. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES Attributes; + HANDLE DriverRegKey = NULL; + UNICODE_STRING TempPath = EMPTY_UNICODE_STRING; + USHORT Index; + + PAGED_CODE(); + + RtlZeroMemory( &NcGlobalData, sizeof( NcGlobalData )); + + // + // Open the mapping registry key. + // + + InitializeObjectAttributes( &Attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + Status = ZwOpenKey( &DriverRegKey, + KEY_READ, + &Attributes ); + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( DriverRegKey == NULL ); + goto NcInitializeMappingCleanup; + } + + Status = NcLoadRegistryString( DriverRegKey, + L"UserMapping", + &TempPath ); + + if (!NT_SUCCESS( Status )) { + goto NcInitializeMappingCleanup; + } + + // + // We check that the name does not contain two + // contiguous slashes. This implies an empty + // name component, which we make no attempt to + // handle. + // + + for (Index = 1; Index < TempPath.Length/sizeof(WCHAR); Index++) { + + if (TempPath.Buffer[Index] == L'\\' && + TempPath.Buffer[Index - 1] == L'\\') { + + Status = STATUS_INVALID_PARAMETER; + goto NcInitializeMappingCleanup; + + } + } + + Status = NcParseFinalComponent( &TempPath, + &NcGlobalData.UserMappingPath, + &NcGlobalData.UserMappingFinalComponentLong ); + + if (!NT_SUCCESS( Status )) { + goto NcInitializeMappingCleanup; + } + + NcFreeUnicodeString( &TempPath ); + + Status = NcLoadRegistryString( DriverRegKey, + L"UserMappingFinalComponentShort", + &NcGlobalData.UserMappingFinalComponentShort ); + + if (!NT_SUCCESS( Status )) { + goto NcInitializeMappingCleanup; + } + + Status = NcLoadRegistryString( DriverRegKey, + L"RealMapping", + &TempPath ); + + if (!NT_SUCCESS( Status )) { + goto NcInitializeMappingCleanup; + } + + // + // We check that the name does not contain two + // contiguous slashes. This implies an empty + // name component, which we make no attempt to + // handle. + // + + for (Index = 1; Index < TempPath.Length/sizeof(WCHAR); Index++) { + + if (TempPath.Buffer[Index] == L'\\' && + TempPath.Buffer[Index - 1] == L'\\') { + + Status = STATUS_INVALID_PARAMETER; + goto NcInitializeMappingCleanup; + + } + } + + Status = NcParseFinalComponent( &TempPath, + &NcGlobalData.RealMappingPath, + &NcGlobalData.RealMappingFinalComponent ); + + if (!NT_SUCCESS( Status )) { + goto NcInitializeMappingCleanup; + } + + // + // We expect out parent mappings to start with '\', and we expect + // no final component to start with '\'. We have already checked + // that the strings contain one WCHAR, but we require two for the + // parent mappings, since the first one is '\'. + // + + if (NcGlobalData.RealMappingPath.Length < (2 * sizeof(WCHAR)) || + NcGlobalData.UserMappingPath.Length < (2 * sizeof(WCHAR)) || + NcGlobalData.RealMappingPath.Buffer[0] != L'\\' || + NcGlobalData.UserMappingPath.Buffer[0] != L'\\' || + NcGlobalData.UserMappingFinalComponentShort.Buffer[0] == L'\\' || + NcGlobalData.UserMappingFinalComponentLong.Buffer[0] == L'\\' || + NcGlobalData.RealMappingFinalComponent.Buffer[0] == L'\\') { + + Status = STATUS_INVALID_PARAMETER; + goto NcInitializeMappingCleanup; + } + + if (!NcIs8DOT3Compatible( &NcGlobalData.UserMappingFinalComponentShort, + &NcGlobalData.UserMappingFinalComponentLong )) { + + Status = STATUS_INVALID_PARAMETER; + goto NcInitializeMappingCleanup; + } + + // + // TODO: This sample assumes that the real mapping final component + // is short. This should not be a required assumption. Note that + // since we only have one component for the real mapping (long and + // short), we don't need to check the long name for compatibility + // with the short name. + // + + if (!NcIs8DOT3Compatible( &NcGlobalData.RealMappingFinalComponent, NULL )) { + + Status = STATUS_INVALID_PARAMETER; + goto NcInitializeMappingCleanup; + } + +NcInitializeMappingCleanup: + + + if (!NT_SUCCESS( Status )) { + + if (NcGlobalData.UserMappingPath.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.UserMappingPath ); + } + + if (NcGlobalData.UserMappingFinalComponentShort.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.UserMappingFinalComponentShort ); + } + + if (NcGlobalData.UserMappingFinalComponentLong.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.UserMappingFinalComponentLong ); + } + + if (NcGlobalData.RealMappingPath.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.RealMappingPath ); + } + + if (NcGlobalData.RealMappingFinalComponent.Buffer != NULL) { + NcFreeUnicodeString( &NcGlobalData.RealMappingFinalComponent ); + } + } + + if (TempPath.Buffer != NULL) { + NcFreeUnicodeString( &TempPath ); + } + + if (DriverRegKey != NULL) { + + NTSTATUS BogusStatus; + + BogusStatus = ZwClose( DriverRegKey ); + FLT_ASSERT(NT_SUCCESS( BogusStatus )); + } + + return Status; +} + diff --git a/filesys/miniFilter/NameChanger/ncmapping.c b/filesys/miniFilter/NameChanger/ncmapping.c new file mode 100644 index 00000000..3dd05135 --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncmapping.c @@ -0,0 +1,796 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + ncmapping.c + +Abstract: + + This module contains helper routines for manipulating mapping + objects. + +Environment: + + Kernel mode + +--*/ + +#include "nc.h" + +BOOLEAN +NcIsMappingPathZeroed ( + _In_ PNC_MAPPING_PATH Path + ); + +NTSTATUS +NcBuildMappingPath ( + _In_ PUNICODE_STRING VolumeName, + _In_ PUNICODE_STRING ParentPath, + _In_ PUNICODE_STRING FinalComponent, + _Inout_ PNC_MAPPING_PATH Path + ); + +NTSTATUS +NcBuildMappingPathFromFile ( + _In_ PFILE_OBJECT Parent, + _In_ PUNICODE_STRING FinalComponent, + _In_ PFLT_INSTANCE Instance, + _In_ BOOLEAN Normalized, + _Inout_ PNC_MAPPING_PATH Path + ); + + +// +// Functions to manage mapping entry +// + +BOOLEAN +NcIsMappingEntryZeroed ( + PNC_MAPPING_ENTRY Entry + ); + +VOID +NcInitMappingEntry ( + _Inout_ PNC_MAPPING_ENTRY Entry + ); + +VOID +NcTeardownMappingEntry ( + _Inout_ PNC_MAPPING_ENTRY Path + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcBuildMappingPath) +#pragma alloc_text(PAGE, NcBuildMappingPathFromFile) +#pragma alloc_text(PAGE, NcBuildMappingPathFromVolume) +#pragma alloc_text(PAGE, NcInitMappingPath) +#pragma alloc_text(PAGE, NcIsMappingPathZeroed) +#pragma alloc_text(PAGE, NcTeardownMappingPath) +#pragma alloc_text(PAGE, NcInitMappingEntry) +#pragma alloc_text(PAGE, NcIsMappingEntryZeroed) +#pragma alloc_text(PAGE, NcTeardownMappingEntry) +#pragma alloc_text(PAGE, NcInitMapping) +#pragma alloc_text(PAGE, NcIsMappingZeroed) +#pragma alloc_text(PAGE, NcTeardownMapping) +#pragma alloc_text(PAGE, NcBuildMapping) +#endif + +// +// Functions to manage the mapping path. +// + +BOOLEAN +NcIsMappingPathZeroed( + _In_ PNC_MAPPING_PATH Path + ) +{ + PAGED_CODE(); + + if (Path->FullPath.Buffer != NULL || + Path->FullPath.Length != 0 || + Path->FullPath.MaximumLength != 0) { + + return FALSE; + } + + if (Path->VolumePath.Buffer != NULL || + Path->VolumePath.Length != 0 || + Path->VolumePath.MaximumLength != 0) { + + return FALSE; + } + + if (Path->ParentPath.Buffer != NULL || + Path->ParentPath.Length != 0 || + Path->ParentPath.MaximumLength != 0) { + + return FALSE; + } + + if (Path->FinalComponentName.Buffer != NULL || + Path->FinalComponentName.Length != 0 || + Path->FinalComponentName.MaximumLength != 0) { + + return FALSE; + } + + if (Path->VolumelessName.Buffer != NULL || + Path->VolumelessName.Length != 0 || + Path->VolumelessName.MaximumLength != 0) { + + return FALSE; + } + + return TRUE; +} + +VOID +NcInitMappingPath ( + _Out_ PNC_MAPPING_PATH Path + ) +/*++ + +Routine Description: + + Routine to initialize a mapping path. + +Arguments: + + Path - Pointer to a user allocated NC_MAPPING_PATH. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + RtlZeroMemory( Path, sizeof( NC_MAPPING_PATH ) ); +} + +VOID +NcTeardownMappingPath ( + _Inout_ PNC_MAPPING_PATH Path + ) +/*++ + +Routine Description: + + Frees the allocations in a NC_MAPPING_PATH. + +Arguments: + + Path - The mapping which you want to clean up. + +Return Value: + + None. + +--*/ +{ + + PAGED_CODE(); + + if (Path->FullPath.Buffer != NULL) { + + ExFreePoolWithTag( Path->FullPath.Buffer, NC_MAPPING_TAG ); + } + + // + // All of the strings point at the same buffer allocation. + // So once we free the buffer in the FullPath, we just zero out + // all of the other strings. + // + + NcInitMappingPath( Path ); +} + +NTSTATUS +NcBuildMappingPath ( + _In_ PUNICODE_STRING VolumeName, + _In_ PUNICODE_STRING ParentPath, + _In_ PUNICODE_STRING FinalComponent, + _Inout_ PNC_MAPPING_PATH Path + ) +/*++ + +Routine Description: + + Builds a NC_MAPPING_PATH from a collection of Unicode strings. + +Arguments: + + VolumeName - Name of the volume. + + ParentPath - string for the parent of mapping. + + FinalComponent - The final component of the mapping path. + + Path - Pointer to a used allocated and zeroed NC_MAPPING_PATH. + +Return Value: + + Returns STATUS_SUCCESS on success. Otherwise returns an error code. + + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + USHORT NameLength; + PWCHAR NameBuffer = NULL; + UNICODE_STRING NameString; + USHORT SeparatorLength; + WCHAR ParentEnd; + USHORT Index; + + PAGED_CODE(); + + FLT_ASSERT( NcIsMappingPathZeroed( Path ) ); + + // + // We cannot remap to/from the root of the volume. + // + + FLT_ASSERT( FinalComponent->Length > 0 ); + + // + // If the parent path does not end with a '\' then we insert one + // between the parent and the final component. + // + + ParentEnd = ParentPath->Buffer[(ParentPath->Length / sizeof(WCHAR)) - 1]; + + if (ParentEnd != NC_SEPARATOR) { + + SeparatorLength = sizeof(WCHAR); + } else { + + SeparatorLength = 0; + } + + // + // Allocate Buffer for Name. + // + + NameLength = VolumeName->Length + ParentPath->Length + SeparatorLength + FinalComponent->Length; + + NameBuffer = ExAllocatePoolWithTag( PagedPool, + NameLength, + NC_MAPPING_TAG ); + + if (NameBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcBuildMappingPathCleanup; + } + + // + // Create String + // + + NameString.Length = 0; + NameString.MaximumLength = NameLength; + NameString.Buffer = NameBuffer; + + // + // Copy Volume Name + // + + RtlCopyUnicodeString( &NameString, VolumeName ); + + // + // Copy Parent Path + // + + RtlAppendUnicodeStringToString( &NameString,ParentPath ); + + // + // Add separator + // + + if (SeparatorLength != 0) { + + NameString.Buffer[NameString.Length/SeparatorLength] = NC_SEPARATOR; + NameString.Length = NameString.Length + SeparatorLength; + FLT_ASSERT( NameString.Length <= NameString.MaximumLength ); + } + + // + // Copy Final Component + // + + RtlAppendUnicodeStringToString( &NameString, FinalComponent ); + + // + // Setup Unicode Strings + // + + Path->FullPath.Buffer = NameString.Buffer; + Path->FullPath.Length = NameString.Length; + Path->FullPath.MaximumLength = NameString.MaximumLength; + + Path->VolumePath.Buffer = NameString.Buffer; + Path->VolumePath.Length = VolumeName->Length; + Path->VolumePath.MaximumLength = VolumeName->Length; + + Path->ParentPath.Buffer = NameString.Buffer; + Path->ParentPath.Length = SeparatorLength == sizeof(WCHAR) ? + VolumeName->Length+ParentPath->Length : + VolumeName->Length+ParentPath->Length-sizeof(WCHAR); + Path->ParentPath.MaximumLength = SeparatorLength == sizeof(WCHAR) ? + VolumeName->Length+ParentPath->Length : + VolumeName->Length+ParentPath->Length-sizeof(WCHAR); + + Path->FinalComponentName.Buffer = (PWSTR)Add2Ptr( NameString.Buffer, + VolumeName->Length+ParentPath->Length+SeparatorLength ); + + Path->FinalComponentName.Length = FinalComponent->Length; + Path->FinalComponentName.MaximumLength = FinalComponent->Length; + + Path->VolumelessName.Buffer = (PWSTR)Add2Ptr( NameString.Buffer, VolumeName->Length ); + Path->VolumelessName.Length = NameString.Length - VolumeName->Length; + Path->VolumelessName.MaximumLength = Path->VolumelessName.Length; + + Path->NumberComponentsInVolumePath = 0; + + for (Index = 0; Index < Path->VolumePath.Length/sizeof(WCHAR); Index++) { + if (Path->VolumePath.Buffer[Index] == L'\\') { + Path->NumberComponentsInVolumePath++; + } + } + + Path->NumberComponentsInFullPath = 0; + + for (Index = 0; Index < Path->FullPath.Length/sizeof(WCHAR); Index++) { + if (Path->FullPath.Buffer[Index] == L'\\') { + Path->NumberComponentsInFullPath++; + } + } + + FLT_ASSERT( Path->NumberComponentsInFullPath > Path->NumberComponentsInVolumePath ); + + // + // We reached the end without incident. + // + + Status = STATUS_SUCCESS; + +NcBuildMappingPathCleanup: + + if (!NT_SUCCESS( Status )) { + + if (NameBuffer != NULL) { + ExFreePoolWithTag( NameBuffer, NC_MAPPING_TAG ); + } + + NcInitMappingPath( Path ); + } + + return Status; +} + +NTSTATUS +NcBuildMappingPathFromVolume ( + _In_ PFLT_VOLUME CONST Volume, + _In_ PUNICODE_STRING ParentPath, + _In_ PUNICODE_STRING FinalComponentName, + _Inout_ PNC_MAPPING_PATH Entry + ) +/*++ + +Routine Description: + + Constructs a path from a Volume and a Caller Specified Path. + +Arguments: + + Volume - Pointer to the volume which the mapping path will be used on. + + ParentPath - Path to the parent of the mapping. + + FinalComponentName - The final component name. + + Entry - Pointer to a user allocated NC_MAPPING_PATH. + +Return Value: + + Returns STATUS_SUCCESS on success. Otherwise returns an error code. + +--*/ +{ + NTSTATUS Status; + ULONG VolumeNameLength; + PVOID VolumeNameBuffer = NULL; + UNICODE_STRING VolumeNameString; + + PAGED_CODE(); + + FLT_ASSERT( NcIsMappingPathZeroed( Entry ) ); + + // + // Query the volume name length. + // + + Status = FltGetVolumeName( Volume, + NULL, + &VolumeNameLength); + + if (!NT_SUCCESS( Status ) && Status != STATUS_BUFFER_TOO_SMALL) { + + goto NcBuildPathCleanup; + } + + // + // Allocate a buffer for the name. + // + + Status = STATUS_SUCCESS; + + VolumeNameBuffer = ExAllocatePoolWithTag( PagedPool, + VolumeNameLength, + NC_MAPPING_TAG ); + + if (VolumeNameBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcBuildPathCleanup; + } + + RtlInitEmptyUnicodeString( &VolumeNameString, VolumeNameBuffer, (USHORT) VolumeNameLength ); + + // + // Query the volume name + // + + Status = FltGetVolumeName( Volume, + &VolumeNameString, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildPathCleanup; + } + + // + // Generate Mapping Path + // + + Status = NcBuildMappingPath( &VolumeNameString, + ParentPath, + FinalComponentName, + Entry ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildPathCleanup; + } + +NcBuildPathCleanup: + + if (!NT_SUCCESS( Status )) { + + NcTeardownMappingPath( Entry ); + } + + if (VolumeNameBuffer != NULL) { + + ExFreePoolWithTag( VolumeNameBuffer, NC_MAPPING_TAG ); + } + + return Status; +} + +NTSTATUS +NcBuildMappingPathFromFile ( + _In_ PFILE_OBJECT Parent, + _In_ PUNICODE_STRING FinalComponent, + _In_ PFLT_INSTANCE Instance, + _In_ BOOLEAN Normalized, + _Inout_ PNC_MAPPING_PATH Path + ) +/*++ + +Routine Description: + + Builds a path from the parent of the mapping and string for the final component. + +Arguments: + + Parent - The file object for the parent of the mapping. + + FinalComponent - A string which is to be used as the final component of the name. + + Instance - The instance for which we are building the mapping. + + NameFlags - Parameters which we will use when checking the name from the file object. + + Path - Pointer to a user allocated NC_MAPPING_PATH which will be populated. + +Return Value + + On success, returns STATUS_SUCCESS, otherwise returns an error code. + +--*/ +{ + NTSTATUS Status; + UNICODE_STRING ParentPath; + PFLT_FILE_NAME_INFORMATION ParentNameInfo = NULL; + FLT_FILE_NAME_OPTIONS NameFlags; + + PAGED_CODE(); + + FLT_ASSERT( NcIsMappingPathZeroed( Path ) ); + + if (Normalized) { + + NameFlags = FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT; + + } else { + + NameFlags = FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT; + + } + + // + // Get File parent's name info. + // + + Status = NcGetFileNameInformation( NULL, + Parent, + Instance, + NameFlags, + &ParentNameInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildMappingPathFromFileCleanup; + } + + Status = FltParseFileNameInformation( ParentNameInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildMappingPathFromFileCleanup; + } + + + FLT_ASSERT( ParentNameInfo->Format == FLT_FILE_NAME_NORMALIZED || + ParentNameInfo->Format == FLT_FILE_NAME_OPENED ); + + + // + // Format Parent Name + // + + ParentPath.Buffer = ParentNameInfo->ParentDir.Buffer; + ParentPath.Length = ParentNameInfo->ParentDir.Length+ParentNameInfo->FinalComponent.Length; + ParentPath.MaximumLength = ParentPath.Length; + + // + // Generate name + // + + Status = NcBuildMappingPath( &ParentNameInfo->Volume, + &ParentPath, + FinalComponent, + Path ); + +NcBuildMappingPathFromFileCleanup: + + if (!NT_SUCCESS( Status )) { + + NcTeardownMappingPath( Path ); + } + + if (ParentNameInfo != NULL) { + + FltReleaseFileNameInformation( ParentNameInfo ); + } + + return Status; +} + +// +// Functions to manage mapping entry +// + +BOOLEAN +NcIsMappingEntryZeroed ( + PNC_MAPPING_ENTRY Entry + ) +{ + PAGED_CODE(); + + if (!NcIsMappingPathZeroed( &Entry->LongNamePath )) + return FALSE; + + if (!NcIsMappingPathZeroed( &Entry->ShortNamePath )) + return FALSE; + + return TRUE; +} + +VOID +NcInitMappingEntry ( + _Inout_ PNC_MAPPING_ENTRY Entry + ) +{ + PAGED_CODE(); + + NcInitMappingPath( &Entry->LongNamePath ); + NcInitMappingPath( &Entry->ShortNamePath ); +} + +VOID +NcTeardownMappingEntry ( + _Inout_ PNC_MAPPING_ENTRY Path + ) +{ + PWCHAR StrBuff = Path->LongNamePath.FullPath.Buffer; + + PAGED_CODE(); + + NcTeardownMappingPath( &Path->LongNamePath ); + + if (StrBuff != Path->ShortNamePath.FullPath.Buffer) { + + // + // There are situations where the two paths point to the + // same buffer. We do this check to make sure that + // we don't double free it. + // + + NcTeardownMappingPath( &Path->ShortNamePath ); + + } else { + + NcInitMappingPath( &Path->ShortNamePath ); + } +} + +// +// Functions to manage mapping +// + +BOOLEAN +NcIsMappingZeroed ( + PNC_MAPPING Mapping + ) +{ + PAGED_CODE(); + if (!NcIsMappingEntryZeroed( &Mapping->RealMapping )) { + + return FALSE; + } + + if (!NcIsMappingEntryZeroed( &Mapping->UserMapping )) { + + return FALSE; + } + + return TRUE; +} + +VOID +NcInitMapping ( + PNC_MAPPING Mapping + ) +{ + PAGED_CODE(); + NcInitMappingEntry( &Mapping->RealMapping ); + NcInitMappingEntry( &Mapping->UserMapping ); +} + +VOID +NcTeardownMapping ( + _Inout_ PNC_MAPPING Mapping + ) +{ + PAGED_CODE(); + NcTeardownMappingEntry( &Mapping->RealMapping ); + NcTeardownMappingEntry( &Mapping->UserMapping ); +} + +NTSTATUS +NcBuildMapping ( + _In_ PFILE_OBJECT UserParent, + _In_ PFILE_OBJECT RealParent, + _In_ PUNICODE_STRING UserFinalComponentShortName, + _In_ PUNICODE_STRING UserFinalComponentLongName, + _In_ PUNICODE_STRING RealFinalComponentName, + _In_ PFLT_INSTANCE Instance, + _Out_ PNC_MAPPING Mapping + ) +/*++ + +Routine Description: + + Constructs the mapping for instance setup. + +Arguments: + + UserParent - File object of the parent of the user mapping. + + RealParent - File object of the parent of the real mapping. + + UserFinalComponentShortName - Short name for the user mapping's final component. + + UserFinalComponentLongName - Long name for the user mapping's final component. + + RealFinalComponentName - Final component name for the real mapping (either long or short). + + Instance - Instance for which we are constructing the mapping. + + Mapping - Pointer to a user allocated NC_MAPPING which will be populated. + +--*/ +{ + + NTSTATUS Status; + PAGED_CODE(); + + FLT_ASSERT( NcIsMappingZeroed( Mapping ) ); + + // + // Build the Real Path. + // + + Status = NcBuildMappingPathFromFile( RealParent, + RealFinalComponentName, + Instance, + TRUE, + &Mapping->RealMapping.LongNamePath ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildMappingCleanup; + } + + // TODO: Do we really know that the real path is 8dot3 compliant? + + // + // Reuse the real path for long and short name. + // + + Mapping->RealMapping.ShortNamePath = + Mapping->RealMapping.LongNamePath; + + // + // Build the user short path. + // + + Status = NcBuildMappingPathFromFile( UserParent, + UserFinalComponentShortName, + Instance, + FALSE, + &Mapping->UserMapping.ShortNamePath ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildMappingCleanup; + } + + // + // Build the user long path. + // + + Status = NcBuildMappingPathFromFile( UserParent, + UserFinalComponentLongName, + Instance, + TRUE, + &Mapping->UserMapping.LongNamePath ); + + if (!NT_SUCCESS( Status )) { + + goto NcBuildMappingCleanup; + } + +NcBuildMappingCleanup: + + return Status; +} + + diff --git a/filesys/miniFilter/NameChanger/ncnameprov.c b/filesys/miniFilter/NameChanger/ncnameprov.c new file mode 100644 index 00000000..1d746388 --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncnameprov.c @@ -0,0 +1,576 @@ + +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcGenerateFileName) +#pragma alloc_text(PAGE, NcNormalizeNameComponentEx) +#endif + +NTSTATUS +NcGenerateFileName ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL OutputNameControl + ) +{ + // + // Status vars + // + + NTSTATUS Status; + + // + // State lookup vars + // + + BOOLEAN Opened = (BOOLEAN)(FileObject->FsContext != NULL); // True if file object is opened. + BOOLEAN ReturnShortName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_SHORT); // True if the user is requesting short name + BOOLEAN ReturnOpenedName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_OPENED); // True if user is requesting opened name. + BOOLEAN ReturnNormalizedName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_NORMALIZED); // True if user is requesting normalized name. + BOOLEAN IgnoreCase; + FLT_FILE_NAME_OPTIONS NameQueryMethod = FltGetFileNameQueryMethod( NameOptions ); + FLT_FILE_NAME_OPTIONS NameFlags = FLT_VALID_FILE_NAME_FLAGS & NameOptions; + + // + // File name information + // + + PFLT_FILE_NAME_INFORMATION LowerNameInfo = NULL; // File name as reported by lower name provider. Will always be down real mapping. + PFLT_FILE_NAME_INFORMATION ShortInfo = NULL; // We will use ShortInfo to store the short name if needed. + + // + // Contexts + // + + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + + // + // Overlap + // + + NC_PATH_OVERLAP RealOverlap; + UNICODE_STRING RealRemainder = EMPTY_UNICODE_STRING; + + // + // Temp storage + // + + UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; + + // + // Temp pointer + // + + PUNICODE_STRING Name = NULL; // Pointer to the name we are going to use. + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + // + // This should never happen, but let's be safe. + // + + if (!ReturnShortName && + !ReturnOpenedName && + !ReturnNormalizedName) { + + FLT_ASSERT( FALSE ); + Status = STATUS_NOT_SUPPORTED; + goto NcGenerateFileNameCleanup; + } + + RealOverlap.EntireFlags = 0; + + // + // To prevent infinite recursion, calls to FltGetFileNameInformation + // from generate file name callbacks should not target current provider. + // + + ClearFlag( NameFlags, FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER ); + + // + // Fetch the instance context. + // + + Status = FltGetInstanceContext( Instance, &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcGenerateFileNameCleanup; + } + + // + // We need to know what the name provider under us thinks the file is called. + // If the caller wants the normalized name we query that, otherwise we query + // the opened name because we have to compare the full path of the file vs. + // the real mapping to determine if the file is mapped. + // + + Status = NcGetFileNameInformation( Data, + FileObject, + Instance, + (ReturnNormalizedName ? FLT_FILE_NAME_NORMALIZED + : FLT_FILE_NAME_OPENED) | + NameQueryMethod | + NameFlags, + &LowerNameInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcGenerateFileNameCleanup; + } + + Status = FltParseFileNameInformation( LowerNameInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcGenerateFileNameCleanup; + } + + // + // Issues With Pre-open path: + // + // 1) Poison name cache below name provider: + // If a filter above a name provider calls FltGetFileNameInformation on a + // file object in his precreate callback, fltmgr will call the name + // provider's generate name callback before the name provider's pre create + // callback is invoked. Name providers by their nature change names in their + // pre-create. Because the name provider has not had the opportunity to + // modify the name yet, we need to make sure that fltmgr does not cache the name we + // return below us, so we set the FLT_FILE_NAME_DO_NOT_CACHE flag. + // //TODO: TRY TO GET ACROSS THAT THIS IS A NAME CHANGER PROBLEM, NOT ALL NAME PROVIDERS NEED TO. + // + + if (!Opened) { + + SetFlag( NameFlags, FLT_FILE_NAME_DO_NOT_CACHE ); + + if (Data) { + + // + // NT supports case sensitive and non-case sensitive naming in file systems. + // This is handled on a per-open basis. Weather an open is case senstive is + // determined by the FO_OPENED_CASE_SENSITIVE flag on the file object. + // In pre-create the SL_CASE_SENSITIVE flag on the create IRP specifies the mode. + // + // If this is on an unopened FileObject, it had better be pre-create so we know + // how to process the operation. If we are queried on an unopened FileObject + // at any other time we have no way to handle the request. + // + + FLT_ASSERT( Data->Iopb->MajorFunction == IRP_MJ_CREATE || + Data->Iopb->MajorFunction == IRP_MJ_NETWORK_QUERY_OPEN ); + + IgnoreCase = !BooleanFlagOn( Data->Iopb->OperationFlags, SL_CASE_SENSITIVE ); + + } else { + + // + // If people do unsafe queries on preopened IOs, we cannot + // determine if the open is case sensitive or not. + // So we cannot determine if this open is down the mapping. + // fail. + // + + FLT_ASSERT( FALSE ); + Status = STATUS_INVALID_PARAMETER; + goto NcGenerateFileNameCleanup; + + } + + } else { + + // + // After a file has been opened, the case sensitivity is stored in the file object. + // + + IgnoreCase = !BooleanFlagOn( FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); + } + + // + // Calculate the overlap with the real mapping. + // + + NcComparePath( &LowerNameInfo->Name, + &InstanceContext->Mapping.RealMapping, + &RealRemainder, + IgnoreCase, + TRUE, + &RealOverlap ); + + // + // Whether we munge depends on what name is requested. + // + + if (ReturnOpenedName || + ReturnNormalizedName) { + + if (Opened && + RealOverlap.InMapping) { + + // + // We munge the opened name if it overlaps with the real mapping. + // The returned path will be down the user mapping. + // + + Status = NcConstructPath( &InstanceContext->Mapping.UserMapping, + &RealRemainder, + TRUE, + &MungedName); + + if (!NT_SUCCESS( Status )) { + + goto NcGenerateFileNameCleanup; + } + + Name = &MungedName; + + } else { + + // + // We return the queried result if the path is not in the + // mapping. + // + + Name = &LowerNameInfo->Name; + + } + + } else if (ReturnShortName) { + + // + // Note that unlike opened names, a query for a shortname only returns + // the final component, not the full path. + // + + // TODO: Assert not preopen + + if (RealOverlap.Match) { + + // + // The opened path is the mapping path. + // This means that if we queried the filesystem + // he would return the wrong path. + // + // Luckily, we can just use the mapping. + // + + Name = &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName; + + } else { + + // + // We have to query below us to get the short name. + // + + Status = NcGetFileNameInformation( Data, + FileObject, + Instance, + FLT_FILE_NAME_SHORT | + NameQueryMethod | + NameFlags, + &ShortInfo ); + + if (!NT_SUCCESS( Status )) { + + goto NcGenerateFileNameCleanup; + } + + Status = FltParseFileNameInformation( ShortInfo ); + + if (!NT_SUCCESS(Status)) { + + goto NcGenerateFileNameCleanup; + } + + // + // Set name to returned name. + // + + Name = &ShortInfo->Name; + } + } + + FLT_ASSERT( Name != NULL ); + + // + // Try to grow the namechanger's record to accommodate the result. + // + + Status = FltCheckAndGrowNameControl( OutputNameControl, Name->Length ); + + if (NT_SUCCESS( Status )) { + + // + // Copy the new name into the buffer. + // + + RtlCopyUnicodeString( &OutputNameControl->Name, Name ); + *CacheFileNameInformation = TRUE; + } + +NcGenerateFileNameCleanup: + + if (LowerNameInfo != NULL) { + + FltReleaseFileNameInformation( LowerNameInfo ); + } + + if (ShortInfo != NULL) { + + FltReleaseFileNameInformation( ShortInfo ); + } + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (MungedName.Buffer != NULL) { + + ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); + } + + return Status; +} + +NTSTATUS +NcNormalizeNameComponentEx ( + _In_ PFLT_INSTANCE Instance, + _In_opt_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +{ + + NTSTATUS Status; + PNC_INSTANCE_CONTEXT InstanceContext = NULL; + NC_PATH_OVERLAP ParentOverlap; //overlap between the parent path and the user mapping. + UNICODE_STRING Remainder; + UNICODE_STRING MungedParentPath; // Path we are going to open for query + PCUNICODE_STRING MungedComponent; // File name to enumerate + PWSTR MungedBuffer = NULL; + ULONG MungedBufferLength; + + IO_STATUS_BLOCK ParentStatusBlock; + OBJECT_ATTRIBUTES ParentAttributes; + HANDLE ParentHandle = 0; + PFILE_OBJECT ParentFileObject = NULL; + BOOLEAN IgnoreCase = !BooleanFlagOn( Flags, FLTFL_NORMALIZE_NAME_CASE_SENSITIVE ); + + PAGED_CODE(); + + FLT_ASSERT( IoGetTopLevelIrp() == NULL ); + + UNREFERENCED_PARAMETER( NormalizationContext ); + UNREFERENCED_PARAMETER( DeviceNameLength ); + UNREFERENCED_PARAMETER( FileObject ); + + Status = FltGetInstanceContext( Instance, + &InstanceContext ); + + if (!NT_SUCCESS( Status )) { + + goto NcNormalizeNameComponentExCleanup; + } + + // + // Default to enumerating the component specified by the + // caller. + // + + MungedComponent = Component; + + NcComparePath( ParentDirectory, + &InstanceContext->Mapping.UserMapping, + &Remainder, + IgnoreCase, + TRUE, + &ParentOverlap ); + + // + // We need to figure out which path we are going to open. + // + + if (ParentOverlap.InMapping) { + + // + // The parent is in the mapping, so it has to be + // munged in order to be opened. + // + + MungedBufferLength = Remainder.Length + InstanceContext->Mapping.RealMapping.LongNamePath.FullPath.Length; + + MungedBuffer = ExAllocatePoolWithTag( PagedPool, MungedBufferLength, NC_NORMALIZE_NAME_TAG ); + + if (MungedBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcNormalizeNameComponentExCleanup; + } + + // + // Construct munged name. + // + + MungedParentPath.Buffer = MungedBuffer; + MungedParentPath.Length = 0; + MungedParentPath.MaximumLength = (USHORT) MungedBufferLength; + + RtlCopyUnicodeString( &MungedParentPath, + &InstanceContext->Mapping.RealMapping.LongNamePath.FullPath ); + + RtlAppendUnicodeStringToString( &MungedParentPath, + &Remainder); + + } else if (ParentOverlap.Parent) { + + // + // The parent is the parent of the user mapping. + // That means we need to see if the final component is + // the mapping path itself. + // + + if( RtlCompareUnicodeString( Component, + &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName, + IgnoreCase) == 0 || + RtlCompareUnicodeString( Component, + &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName, + IgnoreCase) == 0) { + + // + // The requested final component is the mapping itself. + // Hence the real mapping is the munged name. + // + + MungedParentPath = InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath; + MungedComponent = &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName; + + } else { + + // + // The system is requesting another path in the user mapping's parent. + // Go ahead and open the given path. + // + + MungedParentPath = *ParentDirectory; + } + + } else { //ParentOverlap is not within or parent of mapping. + + MungedParentPath = *ParentDirectory; + } + + // + // We should open MungedParentPath and enumerate it. + // + + InitializeObjectAttributes( &ParentAttributes, + &MungedParentPath, + OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0), + NULL, + NULL); + + Status = NcCreateFileHelper( NcGlobalData.FilterHandle, + Instance, + &ParentHandle, + &ParentFileObject, + FILE_LIST_DIRECTORY | FILE_TRAVERSE, + &ParentAttributes, + &ParentStatusBlock, + 0, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OPEN, + FILE_DIRECTORY_FILE, + NULL, + 0, + IO_IGNORE_SHARE_ACCESS_CHECK, + FileObject ); + + if (!NT_SUCCESS( Status )) { + + goto NcNormalizeNameComponentExCleanup; + } + + Status = NcQueryDirectoryFile( Instance, + ParentFileObject, + ExpandComponentName, + ExpandComponentNameLength, + FileNamesInformation, + TRUE, + (PUNICODE_STRING) MungedComponent, + TRUE, + NULL ); + + if (!NT_SUCCESS( Status )) { + + goto NcNormalizeNameComponentExCleanup; + } + + // + // The object exists. Now we need to return the correct + // final name. + // + + if (Component != MungedComponent) { + + ULONG SizeRequired; + + SizeRequired = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName ) + + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length; + + if (ExpandComponentNameLength < SizeRequired) { + + Status = STATUS_BUFFER_OVERFLOW; + goto NcNormalizeNameComponentExCleanup; + } + + FLT_ASSERT( ExpandComponentName->NextEntryOffset == 0 ); + ExpandComponentName->NextEntryOffset = 0; + ExpandComponentName->FileIndex = 0; + ExpandComponentName->FileNameLength = InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length; + RtlCopyMemory( ExpandComponentName->FileName, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Buffer, + InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length ); + + } + + //TODO THIS NEEDS TO + +NcNormalizeNameComponentExCleanup: + + if (InstanceContext != NULL) { + + FltReleaseContext( InstanceContext ); + } + + if (MungedBuffer != NULL) { + + ExFreePoolWithTag( MungedBuffer, NC_NORMALIZE_NAME_TAG ); + } + + if (ParentHandle != 0) { + + FltClose( ParentHandle ); + } + + if (ParentFileObject != NULL) { + + ObDereferenceObject( ParentFileObject ); + + } + + //TODO THERE ARE ONLY TWO VALID ERROR CODES: STATUS_NO_SUCH_FILE, STATUS_SUCCESS if you want the name construction to continue. + //any other code will stop the query. + return Status; +} diff --git a/filesys/miniFilter/NameChanger/ncoffsets.c b/filesys/miniFilter/NameChanger/ncoffsets.c new file mode 100644 index 00000000..f958b21b --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncoffsets.c @@ -0,0 +1,632 @@ +#include "nc.h" + +/* + +Munging entries from the directory query responses requires name changer +to interpret the data returned from the query. There are currently 9 +information classes, each with a different structure for holding the file data. + +Of the 9 queries, we are interested in the following: + +FileBothDirectoryInformation +FileDirectoryInformation +FileFullDirectoryInformation +FileFullDirectoryInformation +FileIdFullDirectoryInformation +FileNamesInformation +FileObjectIdInformation + +Each of structures related to these queries have the following properties: +1) Holds the offset to the next entry +2) Holds the file name length (in bytes) +3) The first WCHAR of the file name is the last entry of the structure, +with the remainder of the name following the structure. + +The purpose of the DIRECTORY_CONTROL_OFFSETS structure is to allow +a generic method of interpreting the query data by holding the offset +information for each of the above variables. Combined with the directory +walking functions (below) we now have an easy uniform means of parsing +the directory queries. + +*/ + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcCalculateDirectoryNotificationOffsets) +#pragma alloc_text(PAGE, NcDetermineStructureOffsets) +#pragma alloc_text(PAGE, NcGetEntrySize) +#pragma alloc_text(PAGE, NcGetFileName) +#pragma alloc_text(PAGE, NcGetFileNameLength) +#pragma alloc_text(PAGE, NcGetNextEntry) +#pragma alloc_text(PAGE, NcGetNextEntryOffset) +#pragma alloc_text(PAGE, NcGetShortName) +#pragma alloc_text(PAGE, NcGetShortNameLength) +#pragma alloc_text(PAGE, NcSetFileName) +#pragma alloc_text(PAGE, NcSetNextEntryOffset) +#pragma alloc_text(PAGE, NcSetShortName) +#endif + +//--------------------------------------------------------------------------- +// FUNCTIONS FOR WALKING DIRECTORY STRUCTURE +//--------------------------------------------------------------------------- +BOOLEAN +NcDetermineStructureOffsets ( + _Out_ PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ FILE_INFORMATION_CLASS Information + ) +/*++ + +Routine Description: + + Determines the structure offsets for the FILE_INFORMATION_CLASS provided. + +Arguments: + + Offsets - Output DIRECTORY_CONTROL_OFFSETS pointer. + + Information - What structure to use. + + DirectoryBuffer - A sample buffer for us to test with. + +Returns: + + return TRUE if we have a structure to handle this request, + otherwise return FALSE. Offsets are returned through the Offsets + pointer. + +--*/ +{ + BOOLEAN ReturnValue = TRUE; + PAGED_CODE(); + + // + // Not all directory information classes have short names. + // So we'll zero out short name data for classes that don't expose it . + // + + Offsets->ShortNamePresent = FALSE; + Offsets->ShortNameLengthDist = 0; + Offsets->ShortNameDist = 0; + + switch( Information ) { + + case FileBothDirectoryInformation: + + Offsets->NextEntryOffsetDist = + FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, NextEntryOffset ); + + Offsets->FileNameLengthDist = + FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileNameLength ); + + Offsets->FileNameDist = + FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName ); + + Offsets->ShortNamePresent = TRUE; + + Offsets->ShortNameLengthDist = + FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, ShortNameLength ); + + Offsets->ShortNameDist = + FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, ShortName ); + + break; + + case FileDirectoryInformation: + + Offsets->NextEntryOffsetDist = + FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, NextEntryOffset ); + + Offsets->FileNameLengthDist = + FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileNameLength ); + + Offsets->FileNameDist = + FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName ); + + break; + + case FileFullDirectoryInformation: + + Offsets->NextEntryOffsetDist = + FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, NextEntryOffset ); + + Offsets->FileNameLengthDist = + FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileNameLength ); + + Offsets->FileNameDist = + FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName ); + + break; + + case FileIdBothDirectoryInformation: + + Offsets->NextEntryOffsetDist = + FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset ); + + Offsets->FileNameLengthDist = + FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, FileNameLength ); + + Offsets->FileNameDist = + FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, FileName ); + + Offsets->ShortNamePresent = TRUE; + + Offsets->ShortNameLengthDist = + FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength ); + + Offsets->ShortNameDist = + FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, ShortName ); + + break; + + case FileIdFullDirectoryInformation: + + Offsets->NextEntryOffsetDist = + FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, NextEntryOffset ); + + Offsets->FileNameLengthDist = + FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, FileNameLength ); + + Offsets->FileNameDist = + FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, FileName ); + + break; + + case FileNamesInformation: + + Offsets->NextEntryOffsetDist = + FIELD_OFFSET( FILE_NAMES_INFORMATION, NextEntryOffset ); + + Offsets->FileNameLengthDist = + FIELD_OFFSET( FILE_NAMES_INFORMATION, FileNameLength ); + + Offsets->FileNameDist = + FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName ); + + break; + + default: + + // + // There are queries that we don't filter, + // so just return FALSE to state failure. + // + + ReturnValue = FALSE; + break; + } + + return ReturnValue; +} + +VOID +NcCalculateDirectoryNotificationOffsets ( + PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Directory Notifications use a structure with similar properties + to a directory query. CalculateDirectoryNotificationOffsets allows + us to reuse the ncoffsets library with directory notification structures. + +Arguments + + Offsets - Output DIRECTORY_CONTROL_OFFSETS pointer. + +--*/ +{ + + PAGED_CODE(); + + // + // Place offsets for next entry and file name. + // + + Offsets->NextEntryOffsetDist = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, NextEntryOffset ); + Offsets->FileNameLengthDist = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileNameLength ); + Offsets->FileNameDist = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName ); + + // + // Directory notification does not have short names. + // + + Offsets->ShortNamePresent = FALSE; + Offsets->ShortNameLengthDist = 0; + Offsets->ShortNameDist = 0; +} + + +ULONG +NcGetNextEntryOffset ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns the offset in bytes to the next entry. + +Arguments: + + Buffer - Pointer to the start of the current entry. + + Offsets - Offsets structure for this information class. + +Return Value: + + The number of bytes from Buffer to the next entry. + +--*/ +{ + PULONG Offset = Add2Ptr( Buffer, Offsets->NextEntryOffsetDist ); + PAGED_CODE(); + return *Offset; +} + + +PVOID +NcGetNextEntry ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns a pointer to the next entry in the buffer. + +Arguments: + + Buffer - Pointer to the start of the current entry. + + Offsets - Offsets structure for this information class. + +Return Value: + + Returns a pointer to the beginning of the next entry. + +--*/ +{ + ULONG Offset = NcGetNextEntryOffset( Buffer, Offsets ); + PVOID NextEntry = Add2Ptr( Buffer, Offset ); + PAGED_CODE(); + return NextEntry; +} + + +ULONG +NcGetFileNameLength ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns the length of the file name in bytes. + +Arguments: + + Buffer - Pointer to the start of the entry. + + Offsets - Offsets structure for the information class. + +Return Value: + + Returns the length of the file name component of the buffer. + +--*/ +{ + ULONG Result = *((PULONG)(Add2Ptr( Buffer, Offsets->FileNameLengthDist ))); + PAGED_CODE(); + return Result; +} + + +ULONG +NcGetEntrySize ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns the size of this entry in bytes. + +Arguments: + + Buffer - Pointer to the start of the entry. + + Offsets - Offsets structure for the information class. + +Return Value: + + Returns the size of this entry in bytes. + +--*/ +{ + ULONG EntrySize = NcGetNextEntryOffset(Buffer, Offsets); + PAGED_CODE(); + + if (EntrySize == 0) { + + // + // We are at last entry, so we need to calculate this ourselves. + // + + EntrySize = NcGetFileNameLength( Buffer, Offsets ); + EntrySize += Offsets->FileNameDist; + } + + return EntrySize; +} + + +PWSTR +NcGetFileName ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns a pointer to the file name string. + +Arguments: + + Buffer - Pointer to the start of the entry. + + Offsets - Offsets structure for the information class. + +Return Value: + + A pointer to the start of the file name for this entry. + +--*/ +{ + PAGED_CODE(); + return Add2Ptr(Buffer, Offsets->FileNameDist); +} + + +PWSTR +NcGetShortName ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns a pointer to the short name for this entry. + +Arguments: + + Buffer - Pointer to the start of this entry. + + Offsets - Offsets structure for the information class. + +Return Value: + + If there is a short name in this information class, returns a pointer to the + start of the short name. Otherwise, returns NULL. +--*/ +{ + PAGED_CODE(); + + if (Offsets->ShortNamePresent) { + + return Add2Ptr(Buffer, Offsets->ShortNameDist); + + } else { + + return NULL; + } +} + + +CCHAR +NcGetShortNameLength ( + _In_ CONST PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Returns the length of the shortname. + +Arguments + + Buffer - Pointer to the start of this entry. + + Offsets - Offsets structure for the information class. + +Return Value + + Returns the length of the short name. + Returns -1 on failure. + +--*/ +{ + CCHAR *Ptr; + PAGED_CODE(); + + if (Offsets->ShortNamePresent) { + + Ptr = Add2Ptr(Buffer, Offsets->ShortNameLengthDist); + return *Ptr; + } else { + + return -1; + } +} + + +VOID +NcSetNextEntryOffset ( + _Inout_ PVOID Buffer, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ BOOLEAN ForceLast + ) +/*++ + +Routine Description: + + Sets the Next Entry Offset to Value. + +Arguments: + + Buffer - Pointer to the start of this entry. + + Offsets - Offsets structure for the information class. + + ForceLast - If true, the size of the next entry is set to zero. + Otherwise, the size will be set to the size of the + information class + the file name's length. + +--*/ +{ + // + // Get pointer to NextEntryOffset value. + // + + PULONG NextEntry = Add2Ptr( Buffer, Offsets->NextEntryOffsetDist ); + PAGED_CODE(); + + // + // Get length of name. + // + + if (ForceLast) { + + // + // This is the last entry, so we need to make sure 0 is in the + // next entry offset. + // + + *NextEntry = 0; + + } else { + + // + // Next entry offset is the distance to the name + // plus the length of the name. + // + + ULONG NameLength = NcGetFileNameLength( Buffer, Offsets ); + *NextEntry = Offsets->FileNameDist + NameLength; + } +} + + +VOID +NcSetFileName ( + _In_ PVOID Entry, + _In_ PWSTR NewName, + _In_ ULONG Length, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets, + _In_ BOOLEAN ForceLast + ) +/*++ + +Routine Description: + + Sets a new file name into the entry. + +Arguments: + + Entry - A pointer to the start of the entry. + + NewName - A pointer to the new file name. + + Length - The length of the new name (in bytes). + + Offsets - Offsets structure for the information class. + + ForceLast - If true, the entry's size will be set to zero + so that it looks like a valid last entry. + +Return Value + + None. + +--*/ +{ + PWSTR NamePtr; + PULONG NameLength; + + PAGED_CODE(); + + // + // Get a pointer to the name in the buffer. + // + + NamePtr = NcGetFileName( Entry, Offsets); + NameLength = Add2Ptr( Entry, Offsets->FileNameLengthDist ); + + // + // Copy the new name into buffer. + // + + RtlCopyMemory( NamePtr, NewName, Length ); + *NameLength = Length; + + // + // Now we have to update the size of this entry. + // + + NcSetNextEntryOffset( Entry, Offsets, ForceLast ); +} + + +VOID +NcSetShortName ( + _In_ PVOID Entry, + _In_ PWSTR NewShortName, + _In_ USHORT Length, + _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets + ) +/*++ + +Routine Description: + + Sets a new short name into an entry if the information class + supports short names. + +Arguments: + + Entry - Pointer to the start of an entry. + + NewShortName - Pointer to the new shortname. + + Length - The length of the short name in bytes. + + Offsets - Offsets structure for the information class. + +Return Value: + + None. + +--*/ +{ + PWSTR NamePtr; + PCCHAR NameLength; + + PAGED_CODE(); + + if( Offsets->ShortNamePresent ) { + + NamePtr = NcGetShortName( Entry, Offsets ); + NameLength = Add2Ptr( Entry, Offsets->ShortNameLengthDist ); + + FLT_ASSERT( Length <= (12 * sizeof(WCHAR)) ); + + RtlCopyMemory(NamePtr, NewShortName, Length ); + *NameLength = (UCHAR) Length; + } +} + diff --git a/filesys/miniFilter/NameChanger/ncpath.c b/filesys/miniFilter/NameChanger/ncpath.c new file mode 100644 index 00000000..4f892e36 --- /dev/null +++ b/filesys/miniFilter/NameChanger/ncpath.c @@ -0,0 +1,674 @@ +#include "nc.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NcComparePath) +#pragma alloc_text(PAGE, NcConstructPath) +#pragma alloc_text(PAGE, NcParseFinalComponent) +#endif + +#define NcIsCharComponentTerminator( C ) \ + ((C) == L'\\' || (C) == L':') + +BOOLEAN +NcComparePath ( + _In_ PCUNICODE_STRING Name, + _In_ PNC_MAPPING_ENTRY Mapping, + _Out_opt_ PUNICODE_STRING Remainder, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ContainsDevice, + _Out_ PNC_PATH_OVERLAP Overlap + ) +/*++ + +Routine Description: + + Compares a name against a mapping and returns the Overlap. + +Arguments: + + Name - The name we are checking. + + Mapping - The mapping path we are comparing against. + + Remainder - If the string matches the mapping, then Remainder will be + the portion of the name after the mapping to the end of the + string. + + IgnoreCase - Case sensitivity. + + ContainsDevice - TRUE if the input name is expected to contain a fully + qualified path including the device name. FALSE if the + path is relative to the root of the volume (excludes + device.) + + Overlap - Pointer to an NC_PATH_OVERLAP structure. Structure members will + be set when this routine exits. + +Return Value: + + TRUE if the string is in the mapping; Remainder is written to if present. + FALSE if the string is not in the mapping; Remainder is not written to. + + Note that this is not a success code; this routine always succeeds. Further + information about the mapping is in the Overlap parameter. + +--*/ +{ + PUNICODE_STRING LongName; + PUNICODE_STRING ShortName; + + PWSTR NameBuff; + PWSTR LongBuff; + PWSTR ShortBuff; + + WCHAR NameBuffCur; + WCHAR LongBuffCur; + WCHAR ShortBuffCur; + + USHORT NameIndex; // Our index in Name + USHORT NameMatchIndex = 0; // Our index of matched components in Name + USHORT LongIndex; // Our index in MappingPath->LongNamePath + USHORT ShortIndex; // Our index in MappingPath->ShortNamePath + + BOOLEAN LongMatch; // TRUE if long name component matches + BOOLEAN ShortMatch; // TRUE if short name component matches + + USHORT NameLength; // Max index for NameIndex + USHORT LongLength; + USHORT ShortLength; + + BOOLEAN NameDone; // TRUE when we hit a '\',stop comparing for this component. + BOOLEAN LongDone; + BOOLEAN ShortDone; + + USHORT ComponentMatches = 0; // Number of component matches. + USHORT MappingComponents; + USHORT VolumeComponents; + + PAGED_CODE(); + + if (ContainsDevice) { + LongName = &Mapping->LongNamePath.FullPath; + ShortName = &Mapping->ShortNamePath.FullPath; + + MappingComponents = Mapping->LongNamePath.NumberComponentsInFullPath; + VolumeComponents = Mapping->LongNamePath.NumberComponentsInVolumePath; + } else { + LongName = &Mapping->LongNamePath.VolumelessName; + ShortName = &Mapping->ShortNamePath.VolumelessName; + + MappingComponents = Mapping->LongNamePath.NumberComponentsInFullPath - Mapping->LongNamePath.NumberComponentsInVolumePath; + VolumeComponents = 0; + } + + NameBuff = Name->Buffer; + LongBuff = LongName->Buffer; + ShortBuff = ShortName->Buffer; + + NameLength = Name->Length/sizeof(WCHAR); // Max index for NameIndex + LongLength = LongName->Length/sizeof(WCHAR); + ShortLength = ShortName->Length/sizeof(WCHAR); + + // + // Names are Unicode strings, so the number of bytes in + // the length should always be even. + // + + FLT_ASSERT( Name->Length % sizeof(WCHAR) == 0 ); + FLT_ASSERT( LongName->Length % sizeof(WCHAR) == 0 ); + FLT_ASSERT( ShortName->Length % sizeof(WCHAR) == 0 ); + FLT_ASSERT( Mapping->LongNamePath.NumberComponentsInFullPath == Mapping->ShortNamePath.NumberComponentsInFullPath ); + + // Set initial values + NameIndex = 0; + LongIndex = 0; + ShortIndex = 0; + NameMatchIndex = 0; + + do // Loop for component scan + { + + // + // Mark that we have not reached the end of the component. + // Once we reach end of component, we stop going forward until everyone has caught up. + // + + NameDone = FALSE; + LongDone = FALSE; + ShortDone = FALSE; + + // + // Mark that the names match + // We assume they match until we prove otherwise. + // + + LongMatch = TRUE; + ShortMatch = TRUE; + + do // Loop for character scan. + { + + // + // See if we have reached the end of the component. + // + + NameDone = (BOOLEAN)(NameIndex >= NameLength || NcIsCharComponentTerminator( NameBuff[NameIndex] )); + LongDone = (BOOLEAN)(LongIndex >= LongLength || LongBuff[LongIndex] == '\\'); + ShortDone = (BOOLEAN)(ShortIndex >= ShortLength || ShortBuff[ShortIndex] == '\\'); + + if (NameDone) { + + // + // If name has run off end, or hit end of component, + // then we need to break. + // + + break; + } + + if(LongDone && ShortDone) { + + // + // If the paths we are comparing against have + // both ether run out or hit the end of their + // respective components, then we need to break. + // + + break; + } + + // + // Convert characters into case insensitive mode (if needed) + // + + if (IgnoreCase) { + + NameBuffCur = RtlUpcaseUnicodeChar( NameBuff[NameIndex] ); + if (!LongDone) { + LongBuffCur = RtlUpcaseUnicodeChar( LongBuff[LongIndex] ); + } + + if (!ShortDone) { + ShortBuffCur = RtlUpcaseUnicodeChar( ShortBuff[ShortIndex] ); + } + + } else { + + NameBuffCur = NameBuff[NameIndex]; + if (!LongDone) { + LongBuffCur = LongBuff[LongIndex]; + } + + if (!ShortDone) { + ShortBuffCur = ShortBuff[ShortIndex]; + } + } + + // + // Compare characters to verify match. + // + + if (!LongDone) { + if (NameBuffCur != LongBuffCur) { + LongMatch = FALSE; // If the characters are not matches, then we are not a match. + } + } else { + LongMatch = FALSE; // If we're comparing beyond the end of the buffer, we don't match. + } + + if (!ShortDone) { + if (NameBuffCur != ShortBuffCur) { + ShortMatch = FALSE; // If the characters are not matches, then we are not a match. + } + } else { + ShortMatch = FALSE; + } + + // + // Move to next index + // + + NameIndex++; + if (!LongDone) { + LongIndex++; + } + if (!ShortDone) { + ShortIndex++; + } + + // + // Loop if there is still chance of match + // + + } while(LongMatch || ShortMatch);// end of character scan. + + // + // We scanned until we had 2 mismatches or we ran off the end of the name, + // or we ran off the end of the long and short names. + // We should scan everyone forward until they are all off the end of their + // buffer, or it the end of their component. + // + + if (NameIndex < NameLength && !NcIsCharComponentTerminator( NameBuff[NameIndex] )) { + + // + // We broke out of character comparison, but were not at the end + // of the name's component. This means that both the long and short + // names were shorter than the name's component. Thus they cannot + // be matches. Furthermore, because neither of them are matches, we + // can stop the search. + // + + LongMatch = FALSE; + ShortMatch = FALSE; + break; + } + + while (NameIndex < NameLength && NameBuff[NameIndex] != '\\') { + + // + // We may still consider this component a match due to a stream. + // In this case, we need to advance the name index until we + // find the next slash. + // + // TODO: Note one effect of this is we will believe + // \dir1\foo matches \dir1:Stream\foo. The latter name is not a + // valid name on any Microsoft filesystem. + // + + NameIndex++; + } + + while( LongIndex < LongLength && LongBuff[LongIndex] != '\\' ) { + + // + // We were not at the end of the long name's component. + // So it cannot be a match. + // Scan forward until we find the end. + // + + LongMatch = FALSE; + LongIndex++; + } + + while( ShortIndex < ShortLength && ShortBuff[ShortIndex] != '\\' ) { + + // + // We were not at the end of the short name's component. + // So it cannot be a match. + // Scan forward until we find the end. + // + + ShortMatch = FALSE; + ShortIndex++; + } + + if ((LongMatch || ShortMatch) && NameIndex != 0) { + ComponentMatches++; + } + + // + // All the indexes should be at the end of their buffer or their component. + // + + FLT_ASSERT( NameIndex == NameLength || NameBuff[NameIndex] == '\\' ); + FLT_ASSERT( LongIndex == LongLength || LongBuff[LongIndex] == '\\' ); + FLT_ASSERT( ShortIndex == ShortLength || ShortBuff[ShortIndex] == '\\' ); + + // + // Since we are all lined up on '\', lets move forward to next component... + // + + if (NameIndex < NameLength) { + NameIndex++; + } + + if (LongIndex < LongLength) { + LongIndex++; + } + + if (ShortIndex < ShortLength) { + ShortIndex++; + } + + if (LongMatch || ShortMatch) { + NameMatchIndex = NameIndex; + } + + // + // Keep looping if name's last component matched either + // the long or short's component and name and the mapping + // have more components. + // + // NOTE: if LongIndex < LongLength, + // then ShortIndex < ShortLength because they have the same number of components. + // + + FLT_ASSERT( (LongIndex < LongLength) ? (ShortIndex < ShortLength) : TRUE ); + + } while( (LongMatch || ShortMatch) && NameIndex < NameLength && LongIndex < LongLength ); + + // + // Now we need to figure out how far we made it, and + // apply the appropriate flags. + // + + Overlap->EntireFlags = 0; + + // + // We can't match more components than exist or something's seriously wrong. + // + FLT_ASSERT( ComponentMatches <= MappingComponents ); + + if (ComponentMatches >= MappingComponents && + NameLength > NameMatchIndex) { + + // + // If we've matched all the components in the mapping and still have data + // left over, we're in the mapping. + // + + Overlap->InMapping = TRUE; + + if (Remainder != NULL) { + + Remainder->Buffer = &NameBuff[NameMatchIndex]; + Remainder->Length = (NameLength - NameMatchIndex) * sizeof(WCHAR); + Remainder->MaximumLength = Remainder->Length; + } + + } else if (ComponentMatches == MappingComponents) { + + // + // If we matched all the components in the mapping and have nothing + // left over, we are the mapping. + // + + Overlap->Match = TRUE; + Overlap->InMapping = TRUE; + + if (Remainder != NULL) { + + Remainder->Buffer = &NameBuff[NameIndex]; + Remainder->MaximumLength = Remainder->Length = 0; + } + + } else if (ComponentMatches == MappingComponents - 1 && + NameLength > NameMatchIndex) { + + // + // If we matched everything except the final component but have data + // left over, we may be a peer of the mapping. For this to be true, + // the only string left must be a filename, not a path. + // + + Overlap->Peer = TRUE; + + for (;NameIndex < NameLength;NameIndex++) { + if (NameBuff[NameIndex] == L'\\') { + Overlap->Peer = FALSE; + break; + } + } + + + } else if (ComponentMatches == MappingComponents - 1) { + + // + // If we matched everything except the final component, then we must + // be the parent. + // + + Overlap->Parent = TRUE; + Overlap->Ancestor = TRUE; + + } else if (ComponentMatches >= VolumeComponents && + NameLength == NameMatchIndex) { + + // + // If we matched something and have no data left over, then we are + // an ancestor path. + // + + Overlap->Ancestor = TRUE; + } + + return (BOOLEAN)Overlap->InMapping; +} + +_Post_satisfies_(NewName->MaximumLength < MAXUSHORT) +_Post_satisfies_(NewName->Length <= NewName->MaximumLength) +_Must_inspect_result_ +NTSTATUS +NcConstructPath ( + _In_ PNC_MAPPING_ENTRY RealPath, + _In_ PUNICODE_STRING Remainder, + _In_ BOOLEAN IncludeVolume, + _Out_ _At_(NewName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING NewName + ) +/*++ + +Routine Description: + + Constructs a path + +Arguments: + + RealPath - Path from the mapping we want to use in name generation. + + Remainder - Remainder string generated by NcComparePath. + + IncludeVolume - If TRUE, the volume will be prepended to the name. + + NewName - Output name. + +Return Value: + + STATUS_SUCCESS or an appropriate error code. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + USHORT NameLength; + PWCHAR NameBuffer; + USHORT SeparatorLength; + + PAGED_CODE(); + + // + // Calculate length of combined name. + // + + SeparatorLength = (Remainder->Length == 0 ? 0 : sizeof(WCHAR)); + NameLength = RealPath->LongNamePath.VolumelessName.Length + SeparatorLength + Remainder->Length; + if( IncludeVolume ) { + + NameLength = NameLength + RealPath->LongNamePath.VolumePath.Length; + } + + // + // Potentially a file may exist on disk which is less than MAXUSHORT, + // but our mapping changes the length such that it now exceeds + // MAXUSHORT. We can't really handle this object meaningfully, since + // all path operations have a UNICODE_STRING limitation. Accordingly, + // some files may be inaccessible for some purposes as a result of + // this. In practice it won't happen, and the important thing is to + // ensure we don't overflow and create security vulnerabilities. + // + if (NameLength >= MAXUSHORT) { + + Status = STATUS_OBJECT_PATH_INVALID; + goto NcConstructPathCleanup; + } + + // + // Allocate space for combined name. + // + + NameBuffer = ExAllocatePoolWithTag( PagedPool, + NameLength, + NC_GENERATE_NAME_TAG ); + + if (NameBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcConstructPathCleanup; + } + + // + // Zero out destination + // + + NewName->Buffer = NameBuffer; + NewName->Length = 0; + NewName->MaximumLength = NameLength; + + // + // Copy Volume Path + // + + if (IncludeVolume) { + + Status = RtlAppendUnicodeStringToString( NewName, &RealPath->LongNamePath.VolumePath ); + + FLT_ASSERT( Status == STATUS_SUCCESS ); + } + + // + // Copy mapping path. + // + + Status = RtlAppendUnicodeStringToString( NewName, + &RealPath->LongNamePath.VolumelessName ); + + FLT_ASSERT( Status == STATUS_SUCCESS ); + + // + // Append separator + // + + if (SeparatorLength > 0) { + + NewName->Buffer[NewName->Length/sizeof(WCHAR)] = NC_SEPARATOR; + NewName->Length += sizeof(WCHAR); + + FLT_ASSERT( NewName->Length <= NewName->MaximumLength ); + } + + // + // Append Remainder. + // + + Status = RtlAppendUnicodeStringToString( NewName, Remainder ); + + FLT_ASSERT( Status == STATUS_SUCCESS ); + +NcConstructPathCleanup: + + return Status; +} + +NTSTATUS +NcParseFinalComponent( + _In_ PUNICODE_STRING EntirePath, + _Out_ PUNICODE_STRING ParentPath, + _Out_ PUNICODE_STRING FinalComponent + ) +{ + USHORT Index = EntirePath->Length / sizeof(WCHAR); + USHORT ParentLength; + USHORT FinalComponentLength; + BOOLEAN FoundFinalComponent = FALSE; + NTSTATUS Status = STATUS_SUCCESS; + PWSTR ParentStringBuffer = NULL; + PWSTR FinalStringBuffer = NULL; + + PAGED_CODE(); + + while(Index > 0) { + Index--; + if (EntirePath->Buffer[Index] == L'\\') { + FoundFinalComponent = TRUE; + break; + } + } + + // + // Paths should contain at least one seperator. + // We expect all paths to be absolute, relative to + // the volume, so they should always start with '\'. + // + if (!FoundFinalComponent) { + return STATUS_INVALID_PARAMETER; + } + + // + // Typically we don't want to include a trailing + // slash in the parent path. The exception to this + // rule is for the volume root. + // + if (Index > 1) { + ParentLength = Index * sizeof(WCHAR); + } else { + ParentLength = (Index + 1) * sizeof(WCHAR); + } + + // + // We also don't want to have no final component. + // This implies a user configured the path as + // \a\b\c\, including the trailing slash. + // + FinalComponentLength = EntirePath->Length - (Index + 1) * sizeof(WCHAR); + if (FinalComponentLength == 0) { + return STATUS_INVALID_PARAMETER; + } + + ParentStringBuffer = ExAllocatePoolWithTag( NonPagedPool, + ParentLength, + NC_TAG ); + + if (ParentStringBuffer == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcParseFinalComponentCleanup; + } + + FinalStringBuffer = ExAllocatePoolWithTag( NonPagedPool, + FinalComponentLength, + NC_TAG ); + + if (FinalStringBuffer == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto NcParseFinalComponentCleanup; + } + + RtlCopyMemory( ParentStringBuffer, EntirePath->Buffer, ParentLength ); + + ParentPath->Buffer = ParentStringBuffer; + ParentPath->MaximumLength = + ParentPath->Length = ParentLength; + + RtlCopyMemory( FinalStringBuffer, &EntirePath->Buffer[Index + 1], FinalComponentLength ); + + FinalComponent->Buffer = FinalStringBuffer; + FinalComponent->MaximumLength = + FinalComponent->Length = FinalComponentLength; + + // + // We've completed successfully, and our allocated + // buffers are in use. + // + ParentStringBuffer = NULL; + FinalStringBuffer = NULL; + +NcParseFinalComponentCleanup: + + if (ParentStringBuffer) { + ExFreePoolWithTag( ParentStringBuffer, NC_TAG ); + } + + if (FinalStringBuffer) { + ExFreePoolWithTag( FinalStringBuffer, NC_TAG ); + } + + return Status; +} diff --git a/filesys/miniFilter/avscan/avscan.sln b/filesys/miniFilter/avscan/avscan.sln index 0fc17fad..d5f16333 100644 --- a/filesys/miniFilter/avscan/avscan.sln +++ b/filesys/miniFilter/avscan/avscan.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{9B4F0964-C698-49FE-B160-251A3A483399}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{D78D0CAE-EF4B-44FC-A328-A04817C7A02D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{4A27FF26-F700-4D97-9F45-F1CBD7C78513}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{8B8F8102-C54A-4300-966E-EC924824AB8A}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "filter\avscan.vcxproj", "{9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "filter\avscan.vcxproj", "{E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "user\avscan.vcxproj", "{23D46B81-CF8D-48E5-BF28-3679E6106D7F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "user\avscan.vcxproj", "{71D634DA-3CBE-46FD-B793-50EE8660A13A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|Win32.ActiveCfg = Debug|Win32 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|Win32.Build.0 = Debug|Win32 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|Win32.ActiveCfg = Release|Win32 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|Win32.Build.0 = Release|Win32 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|x64.ActiveCfg = Debug|x64 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|x64.Build.0 = Debug|x64 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|x64.ActiveCfg = Release|x64 - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|x64.Build.0 = Release|x64 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|Win32.ActiveCfg = Debug|Win32 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|Win32.Build.0 = Debug|Win32 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|Win32.ActiveCfg = Release|Win32 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|Win32.Build.0 = Release|Win32 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|x64.ActiveCfg = Debug|x64 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|x64.Build.0 = Debug|x64 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|x64.ActiveCfg = Release|x64 - {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|x64.Build.0 = Release|x64 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|Win32.Build.0 = Debug|Win32 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|Win32.ActiveCfg = Release|Win32 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|Win32.Build.0 = Release|Win32 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|x64.ActiveCfg = Debug|x64 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|x64.Build.0 = Debug|x64 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|x64.ActiveCfg = Release|x64 + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|x64.Build.0 = Release|x64 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|Win32.ActiveCfg = Debug|Win32 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|Win32.Build.0 = Debug|Win32 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|Win32.ActiveCfg = Release|Win32 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|Win32.Build.0 = Release|Win32 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|x64.ActiveCfg = Debug|x64 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|x64.Build.0 = Debug|x64 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|x64.ActiveCfg = Release|x64 + {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A} = {9B4F0964-C698-49FE-B160-251A3A483399} - {23D46B81-CF8D-48E5-BF28-3679E6106D7F} = {4A27FF26-F700-4D97-9F45-F1CBD7C78513} + {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3} = {D78D0CAE-EF4B-44FC-A328-A04817C7A02D} + {71D634DA-3CBE-46FD-B793-50EE8660A13A} = {8B8F8102-C54A-4300-966E-EC924824AB8A} EndGlobalSection EndGlobal diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj b/filesys/miniFilter/avscan/filter/avscan.vcxproj index d68461a9..1ffed653 100644 --- a/filesys/miniFilter/avscan/filter/avscan.vcxproj +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}</ProjectGuid> + <ProjectGuid>{E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{94FDDA0B-6FFA-4B09-A8F0-A303F46D8BF0}</SampleGuid> + <SampleGuid>{1067E8CA-F2D6-4D98-BC94-B86E241AAA17}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters index 56c60b39..366bc916 100644 --- a/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{6A96A213-242A-44D1-87C3-9EF1E57E7EC9}</UniqueIdentifier> + <UniqueIdentifier>{1A8B08AD-02ED-4F8D-9692-2C34A5799CEE}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{794C3035-6983-4AF3-A981-5EEAA51276B3}</UniqueIdentifier> + <UniqueIdentifier>{8DD0310C-BC99-44D2-A386-3B07A6947AAA}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{A7F78AF9-20F5-4D0E-83A9-8E19405F5C0C}</UniqueIdentifier> + <UniqueIdentifier>{C38EDF08-31B6-4B26-BA29-02805AA5D698}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{0D596D83-E754-4E17-BD2E-884C8CCFCF7D}</UniqueIdentifier> + <UniqueIdentifier>{151004C3-679F-4485-BF94-3CC5842AF213}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj b/filesys/miniFilter/avscan/user/avscan.vcxproj index 7a3112b0..95b470ac 100644 --- a/filesys/miniFilter/avscan/user/avscan.vcxproj +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{23D46B81-CF8D-48E5-BF28-3679E6106D7F}</ProjectGuid> + <ProjectGuid>{71D634DA-3CBE-46FD-B793-50EE8660A13A}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{0D25D92A-41FF-4D6F-A937-A24EE3010777}</SampleGuid> + <SampleGuid>{E649BB1A-4B9F-4AF5-B5E2-9DED21AA79C6}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters index 5ca0df68..75b4df39 100644 --- a/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters @@ -3,15 +3,15 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{7C20F6A5-EEFE-4E1E-ACA6-D81A9175944F}</UniqueIdentifier> + <UniqueIdentifier>{6330BD20-823F-41C7-8759-F6597157F670}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{A175AC1D-BDB6-4F3C-B19E-42160282E929}</UniqueIdentifier> + <UniqueIdentifier>{AC298499-B215-4A19-85AB-4A72B4A56B49}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{B2375B4F-9F4C-4FA5-9C19-76A963F2A7DA}</UniqueIdentifier> + <UniqueIdentifier>{C0A004FA-FFA1-4504-9AF4-3671E5079F64}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.c b/filesys/miniFilter/cancelSafe/cancelSafe.c index 202803ef..d2f4b83d 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.c +++ b/filesys/miniFilter/cancelSafe/cancelSafe.c @@ -1236,6 +1236,20 @@ Return Value: if (!NT_SUCCESS( Status )) { // + // If we failed to queue a workitem we need to + // decrement the worker thread flag. If we did + // not decrement it future queue insertions would + // not trigger a workitem and requests added to + // the queue would be orphaned. We can safely + // decrement the flag here because we are + // guaranteed that no worker routine is currently + // running and that the queue is currently locked. + // + + InterlockedDecrement( &InstCtx->WorkerThreadFlag ); + NT_ASSERT( InstCtx->WorkerThreadFlag == 0 ); + + // // Remove the callback data that was inserted into the queue. // diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.sln b/filesys/miniFilter/cancelSafe/cancelSafe.sln index 17d34d65..a85db67b 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.sln +++ b/filesys/miniFilter/cancelSafe/cancelSafe.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancelSafe", "cancelSafe.vcxproj", "{AC99C662-1480-43BF-B6C1-5B3055F228E2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancelSafe", "cancelSafe.vcxproj", "{17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|Win32.ActiveCfg = Debug|Win32 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|Win32.Build.0 = Debug|Win32 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|Win32.ActiveCfg = Release|Win32 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|Win32.Build.0 = Release|Win32 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|x64.ActiveCfg = Debug|x64 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|x64.Build.0 = Debug|x64 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|x64.ActiveCfg = Release|x64 - {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|x64.Build.0 = Release|x64 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|Win32.ActiveCfg = Debug|Win32 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|Win32.Build.0 = Debug|Win32 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|Win32.ActiveCfg = Release|Win32 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|Win32.Build.0 = Release|Win32 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|x64.ActiveCfg = Debug|x64 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|x64.Build.0 = Debug|x64 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|x64.ActiveCfg = Release|x64 + {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj index 8e6a1960..c5e30e9e 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{AC99C662-1480-43BF-B6C1-5B3055F228E2}</ProjectGuid> + <ProjectGuid>{17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{71FE15A0-262E-47F8-BEFF-3C73167F2349}</SampleGuid> + <SampleGuid>{1A4310B8-0486-411A-876C-A46B9F451585}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters index 11736279..db122ac6 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{4671E7BF-8D01-45C4-A3C1-20A08F5BBEDF}</UniqueIdentifier> + <UniqueIdentifier>{FAEAE193-5200-4618-812C-70AFA581370C}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{ABBB6D26-48D1-4341-AC20-F5D5C5329766}</UniqueIdentifier> + <UniqueIdentifier>{9F8F1EC3-3CF7-4411-BEE9-5DEFA1D2AE58}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{483287C7-E0B5-452F-BB3A-486026CBBB4F}</UniqueIdentifier> + <UniqueIdentifier>{3331A654-2D71-4859-AC8B-FC9157021A59}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{DF4558B9-9BF3-4D98-B89E-718878687D2C}</UniqueIdentifier> + <UniqueIdentifier>{1877FF63-A2D6-4D22-85FF-5158F35E2AAF}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/cdo/cdo.sln b/filesys/miniFilter/cdo/cdo.sln index c0fd8f6d..b6441b60 100644 --- a/filesys/miniFilter/cdo/cdo.sln +++ b/filesys/miniFilter/cdo/cdo.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdo", "cdo.vcxproj", "{1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdo", "cdo.vcxproj", "{4A98BEA4-7D70-4642-814D-B3E69149E496}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|Win32.ActiveCfg = Debug|Win32 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|Win32.Build.0 = Debug|Win32 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|Win32.ActiveCfg = Release|Win32 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|Win32.Build.0 = Release|Win32 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|x64.ActiveCfg = Debug|x64 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|x64.Build.0 = Debug|x64 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|x64.ActiveCfg = Release|x64 - {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|x64.Build.0 = Release|x64 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|Win32.Build.0 = Debug|Win32 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|Win32.ActiveCfg = Release|Win32 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|Win32.Build.0 = Release|Win32 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|x64.ActiveCfg = Debug|x64 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|x64.Build.0 = Debug|x64 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|x64.ActiveCfg = Release|x64 + {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/cdo/cdo.vcxproj b/filesys/miniFilter/cdo/cdo.vcxproj index 52dc8e52..3e5171f4 100644 --- a/filesys/miniFilter/cdo/cdo.vcxproj +++ b/filesys/miniFilter/cdo/cdo.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}</ProjectGuid> + <ProjectGuid>{4A98BEA4-7D70-4642-814D-B3E69149E496}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{6ABBD5BA-9307-4ABF-821A-66D082F38769}</SampleGuid> + <SampleGuid>{59E189BC-6395-42FB-BA5D-85848C0F7789}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/cdo/cdo.vcxproj.Filters b/filesys/miniFilter/cdo/cdo.vcxproj.Filters index 04f6e7f0..9741b152 100644 --- a/filesys/miniFilter/cdo/cdo.vcxproj.Filters +++ b/filesys/miniFilter/cdo/cdo.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{33D6FDE5-707F-4D9E-A316-A031D1F83BC8}</UniqueIdentifier> + <UniqueIdentifier>{4EDD470E-081B-42DB-AC5F-16296A253546}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{3BD87E43-9C30-4470-9754-3114B37E0BFD}</UniqueIdentifier> + <UniqueIdentifier>{B80F0DF0-E538-4454-9BE3-551C8D46C3EE}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{4A9AC38F-BAFE-45E1-929D-047A9F3680ED}</UniqueIdentifier> + <UniqueIdentifier>{35CA4FF2-18D8-4F39-98A2-81E504C473CA}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{AA17AAA7-BDC6-41C8-9342-FF2F9D6AE3CB}</UniqueIdentifier> + <UniqueIdentifier>{D5FEDC88-C91B-4136-8438-A7A7A7A7F1DC}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/change/change.sln b/filesys/miniFilter/change/change.sln index 98e886ed..62a967ed 100644 --- a/filesys/miniFilter/change/change.sln +++ b/filesys/miniFilter/change/change.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "change", "change.vcxproj", "{0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "change", "change.vcxproj", "{60E86F7F-16E4-4F3C-91A5-F8E202386F10}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|Win32.ActiveCfg = Debug|Win32 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|Win32.Build.0 = Debug|Win32 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|Win32.ActiveCfg = Release|Win32 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|Win32.Build.0 = Release|Win32 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|x64.ActiveCfg = Debug|x64 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|x64.Build.0 = Debug|x64 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|x64.ActiveCfg = Release|x64 - {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|x64.Build.0 = Release|x64 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|Win32.ActiveCfg = Debug|Win32 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|Win32.Build.0 = Debug|Win32 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|Win32.ActiveCfg = Release|Win32 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|Win32.Build.0 = Release|Win32 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|x64.ActiveCfg = Debug|x64 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|x64.Build.0 = Debug|x64 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|x64.ActiveCfg = Release|x64 + {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/change/change.vcxproj b/filesys/miniFilter/change/change.vcxproj index d16c83bf..7f34ea33 100644 --- a/filesys/miniFilter/change/change.vcxproj +++ b/filesys/miniFilter/change/change.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}</ProjectGuid> + <ProjectGuid>{60E86F7F-16E4-4F3C-91A5-F8E202386F10}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{1794254B-AC98-41EB-A845-110FCD1AA932}</SampleGuid> + <SampleGuid>{04E1BD24-A9C6-471E-942D-B4A4F77125FE}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/change/change.vcxproj.Filters b/filesys/miniFilter/change/change.vcxproj.Filters index 1bca247f..ebad34d5 100644 --- a/filesys/miniFilter/change/change.vcxproj.Filters +++ b/filesys/miniFilter/change/change.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{D9D46B85-B0A5-4C33-8BE4-28566220F42D}</UniqueIdentifier> + <UniqueIdentifier>{68724F11-A0FA-48CE-85FD-A786F328F757}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{B66FFE81-FB9C-4422-BF7B-795BA16B48F1}</UniqueIdentifier> + <UniqueIdentifier>{FEAB8F27-1AD0-4230-BB82-A9FA8EF7DF67}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{78FB7669-0DC6-4483-9EAB-571950829035}</UniqueIdentifier> + <UniqueIdentifier>{E66AFE53-4910-4644-A9A4-63861F5375BF}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{2E70873B-AF69-4324-ACF7-7318A567F6B3}</UniqueIdentifier> + <UniqueIdentifier>{380AD108-3AC4-428B-8916-067667866507}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/ctx/ctx.sln b/filesys/miniFilter/ctx/ctx.sln index 316647ca..8dfec33a 100644 --- a/filesys/miniFilter/ctx/ctx.sln +++ b/filesys/miniFilter/ctx/ctx.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctx", "ctx.vcxproj", "{8A826E76-F53C-4951-B81A-8653A4FC7BF0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctx", "ctx.vcxproj", "{D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|Win32.ActiveCfg = Debug|Win32 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|Win32.Build.0 = Debug|Win32 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|Win32.ActiveCfg = Release|Win32 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|Win32.Build.0 = Release|Win32 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|x64.ActiveCfg = Debug|x64 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|x64.Build.0 = Debug|x64 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|x64.ActiveCfg = Release|x64 - {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|x64.Build.0 = Release|x64 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|Win32.ActiveCfg = Debug|Win32 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|Win32.Build.0 = Debug|Win32 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|Win32.ActiveCfg = Release|Win32 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|Win32.Build.0 = Release|Win32 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|x64.ActiveCfg = Debug|x64 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|x64.Build.0 = Debug|x64 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|x64.ActiveCfg = Release|x64 + {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/ctx/ctx.vcxproj b/filesys/miniFilter/ctx/ctx.vcxproj index 65ce09b0..02105e0a 100644 --- a/filesys/miniFilter/ctx/ctx.vcxproj +++ b/filesys/miniFilter/ctx/ctx.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{8A826E76-F53C-4951-B81A-8653A4FC7BF0}</ProjectGuid> + <ProjectGuid>{D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{E0BB0921-D467-4DDA-82CD-8B1FAADDBC55}</SampleGuid> + <SampleGuid>{EA561ABD-0088-4EF5-875C-CE05C0BC76D4}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/ctx/ctx.vcxproj.Filters b/filesys/miniFilter/ctx/ctx.vcxproj.Filters index 4eef3fc4..22a08147 100644 --- a/filesys/miniFilter/ctx/ctx.vcxproj.Filters +++ b/filesys/miniFilter/ctx/ctx.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{872ED434-D252-450B-ACD5-B7DDB6FF6D4E}</UniqueIdentifier> + <UniqueIdentifier>{36F385EE-28D4-41AE-A395-DD2DDE392541}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{FC7A2108-DAAB-43D9-AEFA-120FC32876D7}</UniqueIdentifier> + <UniqueIdentifier>{B72DF203-BB8E-4EA1-AAA9-F25126CC8C2B}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{C1AC955C-1C1A-4B66-8AA8-C6173E6B2EDD}</UniqueIdentifier> + <UniqueIdentifier>{6968CBCC-DC1D-49E1-BCBE-1FBAC7ED57E6}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{1E203714-FFC5-4465-BC3D-2ED22ED46D83}</UniqueIdentifier> + <UniqueIdentifier>{C038C42C-4536-4CB3-8645-69BE91B63491}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/delete/delete.sln b/filesys/miniFilter/delete/delete.sln index 3af04bb5..2c13b91b 100644 --- a/filesys/miniFilter/delete/delete.sln +++ b/filesys/miniFilter/delete/delete.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "delete", "delete.vcxproj", "{A872AE22-1A64-4531-A85F-03361BE73894}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "delete", "delete.vcxproj", "{70924271-22E0-474E-BB48-4176AB921E8F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|Win32.ActiveCfg = Debug|Win32 - {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|Win32.Build.0 = Debug|Win32 - {A872AE22-1A64-4531-A85F-03361BE73894}.Release|Win32.ActiveCfg = Release|Win32 - {A872AE22-1A64-4531-A85F-03361BE73894}.Release|Win32.Build.0 = Release|Win32 - {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|x64.ActiveCfg = Debug|x64 - {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|x64.Build.0 = Debug|x64 - {A872AE22-1A64-4531-A85F-03361BE73894}.Release|x64.ActiveCfg = Release|x64 - {A872AE22-1A64-4531-A85F-03361BE73894}.Release|x64.Build.0 = Release|x64 + {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|Win32.ActiveCfg = Debug|Win32 + {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|Win32.Build.0 = Debug|Win32 + {70924271-22E0-474E-BB48-4176AB921E8F}.Release|Win32.ActiveCfg = Release|Win32 + {70924271-22E0-474E-BB48-4176AB921E8F}.Release|Win32.Build.0 = Release|Win32 + {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|x64.ActiveCfg = Debug|x64 + {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|x64.Build.0 = Debug|x64 + {70924271-22E0-474E-BB48-4176AB921E8F}.Release|x64.ActiveCfg = Release|x64 + {70924271-22E0-474E-BB48-4176AB921E8F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/delete/delete.vcxproj b/filesys/miniFilter/delete/delete.vcxproj index c2863872..b827a7f2 100644 --- a/filesys/miniFilter/delete/delete.vcxproj +++ b/filesys/miniFilter/delete/delete.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{A872AE22-1A64-4531-A85F-03361BE73894}</ProjectGuid> + <ProjectGuid>{70924271-22E0-474E-BB48-4176AB921E8F}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{DDF5DA16-05BC-4E87-9743-B11BFFA79395}</SampleGuid> + <SampleGuid>{983B8DC5-A107-4899-88AE-7E3EF643B95B}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/delete/delete.vcxproj.Filters b/filesys/miniFilter/delete/delete.vcxproj.Filters index 994af6a4..2502ad88 100644 --- a/filesys/miniFilter/delete/delete.vcxproj.Filters +++ b/filesys/miniFilter/delete/delete.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{0433160E-D11D-43E0-962A-F91BE56E9913}</UniqueIdentifier> + <UniqueIdentifier>{18043465-F44B-4DEF-8BAE-4836B5B96347}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{1BDAC8A0-7CBD-4CC1-9BC9-531A473E2C93}</UniqueIdentifier> + <UniqueIdentifier>{1305C3BE-0E2E-4103-8DFB-4B06F55C5FD5}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{391C5CBD-0397-4FCB-AFAE-CBDE3F118A7F}</UniqueIdentifier> + <UniqueIdentifier>{C66CE925-8A7F-4B95-B92B-E3EC6C00BD69}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{1791F586-ED65-4309-8E44-3A9A74B4E2D7}</UniqueIdentifier> + <UniqueIdentifier>{D2AA6CEC-4B22-4779-B62F-D83B3AB88B56}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj b/filesys/miniFilter/minispy/filter/minispy.vcxproj index de6dac81..a75aceb5 100644 --- a/filesys/miniFilter/minispy/filter/minispy.vcxproj +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{99B46F3E-1CC2-4689-8D3E-80CCBD448E39}</ProjectGuid> + <ProjectGuid>{F59D1435-1559-4BFF-90FF-F6B2E081ADBA}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{88181562-E49D-4E01-B470-89CECAA14E2E}</SampleGuid> + <SampleGuid>{B0F09019-3767-45D4-87E2-FF17FA11652A}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters index 7425e30c..4c51d4ff 100644 --- a/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{3973B07F-3667-4849-9D74-E6225E72C259}</UniqueIdentifier> + <UniqueIdentifier>{ADEB4B1F-7275-4D86-A8AC-1268384B7530}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{7BA4662B-3492-4B81-8081-ACA773397EA2}</UniqueIdentifier> + <UniqueIdentifier>{AF75460A-0706-4819-B584-BA557565BD9E}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{7C19E51C-41FA-46DE-9B69-35A11D9989A7}</UniqueIdentifier> + <UniqueIdentifier>{57EBC54C-C6CD-46B7-8093-C4810A40C233}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{E81C4500-8F1F-45E5-92F1-B012E4D9FEB7}</UniqueIdentifier> + <UniqueIdentifier>{32B54034-2B3E-4AE9-9F0D-2D256FC4276D}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/minispy/minispy.sln b/filesys/miniFilter/minispy/minispy.sln index f6bc3c1c..22ea2743 100644 --- a/filesys/miniFilter/minispy/minispy.sln +++ b/filesys/miniFilter/minispy/minispy.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{3B396921-0231-4B6C-B5E5-C7695F992BA7}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{39A7F6C2-D480-4C5C-B6FB-48ACDDA45B59}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{32B4D5F7-B865-467E-9344-627DDBB25267}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{42CC0135-35A4-4AD0-97D5-140EA747A3CD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "filter\minispy.vcxproj", "{99B46F3E-1CC2-4689-8D3E-80CCBD448E39}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "filter\minispy.vcxproj", "{F59D1435-1559-4BFF-90FF-F6B2E081ADBA}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "user\minispy.vcxproj", "{3EFB308B-ED6C-42FE-9140-9883674EA0A2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "user\minispy.vcxproj", "{8193E678-7717-4820-BBA6-A15A283160B1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|Win32.ActiveCfg = Debug|Win32 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|Win32.Build.0 = Debug|Win32 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|Win32.ActiveCfg = Release|Win32 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|Win32.Build.0 = Release|Win32 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|x64.ActiveCfg = Debug|x64 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|x64.Build.0 = Debug|x64 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|x64.ActiveCfg = Release|x64 - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|x64.Build.0 = Release|x64 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|Win32.ActiveCfg = Debug|Win32 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|Win32.Build.0 = Debug|Win32 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|Win32.ActiveCfg = Release|Win32 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|Win32.Build.0 = Release|Win32 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|x64.ActiveCfg = Debug|x64 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|x64.Build.0 = Debug|x64 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|x64.ActiveCfg = Release|x64 - {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|x64.Build.0 = Release|x64 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|Win32.ActiveCfg = Debug|Win32 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|Win32.Build.0 = Debug|Win32 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|Win32.ActiveCfg = Release|Win32 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|Win32.Build.0 = Release|Win32 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|x64.ActiveCfg = Debug|x64 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|x64.Build.0 = Debug|x64 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|x64.ActiveCfg = Release|x64 + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|x64.Build.0 = Release|x64 + {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|Win32.ActiveCfg = Debug|Win32 + {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|Win32.Build.0 = Debug|Win32 + {8193E678-7717-4820-BBA6-A15A283160B1}.Release|Win32.ActiveCfg = Release|Win32 + {8193E678-7717-4820-BBA6-A15A283160B1}.Release|Win32.Build.0 = Release|Win32 + {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|x64.ActiveCfg = Debug|x64 + {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|x64.Build.0 = Debug|x64 + {8193E678-7717-4820-BBA6-A15A283160B1}.Release|x64.ActiveCfg = Release|x64 + {8193E678-7717-4820-BBA6-A15A283160B1}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {99B46F3E-1CC2-4689-8D3E-80CCBD448E39} = {3B396921-0231-4B6C-B5E5-C7695F992BA7} - {3EFB308B-ED6C-42FE-9140-9883674EA0A2} = {32B4D5F7-B865-467E-9344-627DDBB25267} + {F59D1435-1559-4BFF-90FF-F6B2E081ADBA} = {39A7F6C2-D480-4C5C-B6FB-48ACDDA45B59} + {8193E678-7717-4820-BBA6-A15A283160B1} = {42CC0135-35A4-4AD0-97D5-140EA747A3CD} EndGlobalSection EndGlobal diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj b/filesys/miniFilter/minispy/user/minispy.vcxproj index 7903c881..14af62cc 100644 --- a/filesys/miniFilter/minispy/user/minispy.vcxproj +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{3EFB308B-ED6C-42FE-9140-9883674EA0A2}</ProjectGuid> + <ProjectGuid>{8193E678-7717-4820-BBA6-A15A283160B1}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{66B7506B-24F4-4F68-9941-68114AD91305}</SampleGuid> + <SampleGuid>{FF833B16-7C46-43F5-911C-228791B84F56}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters index dff75ebd..e9cd4bba 100644 --- a/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters @@ -3,15 +3,15 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{FE182DA6-B396-42B5-BA36-09DA649AD386}</UniqueIdentifier> + <UniqueIdentifier>{1FD3EC74-2C96-4C1D-BB60-19DC7C1F0008}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{72009667-0B4E-4D0D-9548-CB2182BD6769}</UniqueIdentifier> + <UniqueIdentifier>{0B6C07D3-9F4A-499D-B097-2BC02C31091A}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{9B01DDB6-745D-4785-BD95-F02013721E53}</UniqueIdentifier> + <UniqueIdentifier>{36CB183B-6EAB-4767-957A-87186CA11D68}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/nullFilter/nullFilter.sln b/filesys/miniFilter/nullFilter/nullFilter.sln index 9af52d43..1a5db6f9 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.sln +++ b/filesys/miniFilter/nullFilter/nullFilter.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nullFilter", "nullFilter.vcxproj", "{83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nullFilter", "nullFilter.vcxproj", "{50FE4D46-70DC-4104-8360-9E84D58B1ABD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|Win32.ActiveCfg = Debug|Win32 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|Win32.Build.0 = Debug|Win32 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|Win32.ActiveCfg = Release|Win32 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|Win32.Build.0 = Release|Win32 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|x64.ActiveCfg = Debug|x64 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|x64.Build.0 = Debug|x64 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|x64.ActiveCfg = Release|x64 - {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|x64.Build.0 = Release|x64 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|Win32.ActiveCfg = Debug|Win32 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|Win32.Build.0 = Debug|Win32 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|Win32.ActiveCfg = Release|Win32 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|Win32.Build.0 = Release|Win32 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|x64.ActiveCfg = Debug|x64 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|x64.Build.0 = Debug|x64 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|x64.ActiveCfg = Release|x64 + {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj b/filesys/miniFilter/nullFilter/nullFilter.vcxproj index 7e24749f..68b1d5da 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.vcxproj +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}</ProjectGuid> + <ProjectGuid>{50FE4D46-70DC-4104-8360-9E84D58B1ABD}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{6431C66E-F38F-45F1-BE1E-D06503CE7799}</SampleGuid> + <SampleGuid>{0B2B36C6-26D3-49D5-962E-C42C0A37B0D7}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters index a199c829..33afb077 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{AC14DB33-6BC1-44EC-AAB1-816BA496A388}</UniqueIdentifier> + <UniqueIdentifier>{F188061D-FE31-43EE-A670-3276C6F0AD16}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{F9EA13FF-7409-4FCD-B877-4F877D4E24BF}</UniqueIdentifier> + <UniqueIdentifier>{11EA27C6-A0E0-4F0A-8981-6592E27784D9}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{365A22DE-D53C-4640-8237-5E79E158A537}</UniqueIdentifier> + <UniqueIdentifier>{03863453-B4FD-4A2F-AA85-A2448B28F0DC}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{8F416348-B401-4C11-9DD0-4D79812667AD}</UniqueIdentifier> + <UniqueIdentifier>{714F8ED5-D3FD-4FDF-9650-251E97E2D0B6}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/passThrough/passThrough.sln b/filesys/miniFilter/passThrough/passThrough.sln index 26698330..1d49e685 100644 --- a/filesys/miniFilter/passThrough/passThrough.sln +++ b/filesys/miniFilter/passThrough/passThrough.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passThrough", "passThrough.vcxproj", "{6925DFEC-4D62-4106-9465-F4BFC1CA280B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passThrough", "passThrough.vcxproj", "{8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|Win32.ActiveCfg = Debug|Win32 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|Win32.Build.0 = Debug|Win32 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|Win32.ActiveCfg = Release|Win32 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|Win32.Build.0 = Release|Win32 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|x64.ActiveCfg = Debug|x64 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|x64.Build.0 = Debug|x64 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|x64.ActiveCfg = Release|x64 - {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|x64.Build.0 = Release|x64 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|Win32.ActiveCfg = Debug|Win32 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|Win32.Build.0 = Debug|Win32 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|Win32.ActiveCfg = Release|Win32 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|Win32.Build.0 = Release|Win32 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|x64.ActiveCfg = Debug|x64 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|x64.Build.0 = Debug|x64 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|x64.ActiveCfg = Release|x64 + {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj b/filesys/miniFilter/passThrough/passThrough.vcxproj index 66521b00..cef246fc 100644 --- a/filesys/miniFilter/passThrough/passThrough.vcxproj +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{6925DFEC-4D62-4106-9465-F4BFC1CA280B}</ProjectGuid> + <ProjectGuid>{8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{D0990CEE-D681-43CD-9797-60AB0D628853}</SampleGuid> + <SampleGuid>{43A19993-BF9D-4DAD-A4CB-892DA37962D9}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters index 24e2fc4c..53c358b3 100644 --- a/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{D8FC9FC6-EBA8-4607-B388-0F5F1CACBA9C}</UniqueIdentifier> + <UniqueIdentifier>{A3FF7F10-8FB5-4702-A04E-1FB975F5F7BC}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{968D5F4E-042F-4EB1-89CA-8A4DC5FAD3B8}</UniqueIdentifier> + <UniqueIdentifier>{A60ED505-D6D8-4912-8E60-31F9A98408D4}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{BD7D5A5F-EEB5-43CF-AEF9-602F292252B7}</UniqueIdentifier> + <UniqueIdentifier>{6E1D373B-63DD-434D-9219-9E9F06913A45}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{6C7310BB-F3EA-4F55-A624-111FE5C6DF8D}</UniqueIdentifier> + <UniqueIdentifier>{48A890C1-A2B2-447D-8746-A08B9C046052}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj b/filesys/miniFilter/scanner/filter/scanner.vcxproj index 99432692..9f737bb8 100644 --- a/filesys/miniFilter/scanner/filter/scanner.vcxproj +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}</ProjectGuid> + <ProjectGuid>{6DA01722-898E-478D-AA5D-7477B31F2E3C}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{82607450-8E93-44E4-9EDC-AA0C310E0E64}</SampleGuid> + <SampleGuid>{3C220F60-96A1-444C-A61D-4CD384F249A2}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters index 37511409..f33f5037 100644 --- a/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{714BE986-D639-4C08-B0B5-CEDD7F5A24B3}</UniqueIdentifier> + <UniqueIdentifier>{21DA36D6-0604-456B-948A-ED26A6DE69DE}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{8E4DEA17-46F6-459E-91CA-8EB73909B60A}</UniqueIdentifier> + <UniqueIdentifier>{4DE93ED6-CF7E-4EED-90C7-AFED0DC52266}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{A4702A45-F819-43D0-B05B-56FFCA504075}</UniqueIdentifier> + <UniqueIdentifier>{59D2E5FD-012C-4647-A2E2-61A3E8E63CA5}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{A873A433-3F6A-4828-9BDA-9FDF6C9D4726}</UniqueIdentifier> + <UniqueIdentifier>{CDB9F3CF-9FAB-4B3D-A857-7D6FA6680A05}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/scanner/scanner.sln b/filesys/miniFilter/scanner/scanner.sln index f79705b4..5414efd7 100644 --- a/filesys/miniFilter/scanner/scanner.sln +++ b/filesys/miniFilter/scanner/scanner.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{826E5DA7-B03B-43C0-9CFF-E0B6E29942A3}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{FD39AB44-4D29-4235-AF0B-25A200E98950}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{B27B6441-52BC-4DFD-9A25-1BC5972664F4}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{38839ABD-FCE8-455C-AFAC-3AF46756066C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanner", "filter\scanner.vcxproj", "{6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanner", "filter\scanner.vcxproj", "{6DA01722-898E-478D-AA5D-7477B31F2E3C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanuser", "user\scanuser.vcxproj", "{EF224A50-E448-4767-AD5E-1EEF058D0E50}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanuser", "user\scanuser.vcxproj", "{F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|Win32.ActiveCfg = Debug|Win32 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|Win32.Build.0 = Debug|Win32 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|Win32.ActiveCfg = Release|Win32 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|Win32.Build.0 = Release|Win32 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|x64.ActiveCfg = Debug|x64 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|x64.Build.0 = Debug|x64 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|x64.ActiveCfg = Release|x64 - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|x64.Build.0 = Release|x64 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|Win32.ActiveCfg = Debug|Win32 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|Win32.Build.0 = Debug|Win32 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|Win32.ActiveCfg = Release|Win32 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|Win32.Build.0 = Release|Win32 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|x64.ActiveCfg = Debug|x64 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|x64.Build.0 = Debug|x64 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|x64.ActiveCfg = Release|x64 - {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|x64.Build.0 = Release|x64 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|Win32.Build.0 = Debug|Win32 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|Win32.ActiveCfg = Release|Win32 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|Win32.Build.0 = Release|Win32 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|x64.ActiveCfg = Debug|x64 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|x64.Build.0 = Debug|x64 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|x64.ActiveCfg = Release|x64 + {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|x64.Build.0 = Release|x64 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|Win32.ActiveCfg = Debug|Win32 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|Win32.Build.0 = Debug|Win32 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|Win32.ActiveCfg = Release|Win32 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|Win32.Build.0 = Release|Win32 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|x64.ActiveCfg = Debug|x64 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|x64.Build.0 = Debug|x64 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|x64.ActiveCfg = Release|x64 + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3} = {826E5DA7-B03B-43C0-9CFF-E0B6E29942A3} - {EF224A50-E448-4767-AD5E-1EEF058D0E50} = {B27B6441-52BC-4DFD-9A25-1BC5972664F4} + {6DA01722-898E-478D-AA5D-7477B31F2E3C} = {FD39AB44-4D29-4235-AF0B-25A200E98950} + {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB} = {38839ABD-FCE8-455C-AFAC-3AF46756066C} EndGlobalSection EndGlobal diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj b/filesys/miniFilter/scanner/user/scanuser.vcxproj index a60beb42..dbab3266 100644 --- a/filesys/miniFilter/scanner/user/scanuser.vcxproj +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{EF224A50-E448-4767-AD5E-1EEF058D0E50}</ProjectGuid> + <ProjectGuid>{F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{2640B41E-C043-4A8E-B6EE-136BFFA9D855}</SampleGuid> + <SampleGuid>{F6714426-3D4C-4E07-AF32-F6C97BB4E3A6}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters index 468b6701..0a7635cc 100644 --- a/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters @@ -3,15 +3,15 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{2CE1DA5C-939F-4E19-AECC-C2BF08201270}</UniqueIdentifier> + <UniqueIdentifier>{2CC6A14A-A1EB-4502-B4F5-DEC93B1962CD}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{5E1F3D4B-5B51-4AD3-937E-D0FD3FC41DEC}</UniqueIdentifier> + <UniqueIdentifier>{FC5F9B5A-4DE1-4EFB-A76D-C4E632BF4E43}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{FCEF13F3-09F2-4ED0-9EA6-455F60759ED5}</UniqueIdentifier> + <UniqueIdentifier>{F349C8BC-1724-4FC6-AB45-98CE70B47971}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/simrep/simrep.sln b/filesys/miniFilter/simrep/simrep.sln index 179a1e3c..f2a7496b 100644 --- a/filesys/miniFilter/simrep/simrep.sln +++ b/filesys/miniFilter/simrep/simrep.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simrep", "simrep.vcxproj", "{8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simrep", "simrep.vcxproj", "{6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|Win32.ActiveCfg = Debug|Win32 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|Win32.Build.0 = Debug|Win32 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|Win32.ActiveCfg = Release|Win32 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|Win32.Build.0 = Release|Win32 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|x64.ActiveCfg = Debug|x64 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|x64.Build.0 = Debug|x64 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|x64.ActiveCfg = Release|x64 - {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|x64.Build.0 = Release|x64 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|Win32.ActiveCfg = Debug|Win32 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|Win32.Build.0 = Debug|Win32 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|Win32.ActiveCfg = Release|Win32 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|Win32.Build.0 = Release|Win32 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|x64.ActiveCfg = Debug|x64 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|x64.Build.0 = Debug|x64 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|x64.ActiveCfg = Release|x64 + {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/simrep/simrep.vcxproj b/filesys/miniFilter/simrep/simrep.vcxproj index 41f903f0..c9005083 100644 --- a/filesys/miniFilter/simrep/simrep.vcxproj +++ b/filesys/miniFilter/simrep/simrep.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}</ProjectGuid> + <ProjectGuid>{6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{E65EC005-19A7-464F-8CA6-C0A78FD9F8C8}</SampleGuid> + <SampleGuid>{1A3F112D-0CEE-4178-81FF-E9B1C64AF142}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/simrep/simrep.vcxproj.Filters b/filesys/miniFilter/simrep/simrep.vcxproj.Filters index e5b8f9ad..290230b8 100644 --- a/filesys/miniFilter/simrep/simrep.vcxproj.Filters +++ b/filesys/miniFilter/simrep/simrep.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{D5037E9C-B07A-4282-8BD3-F0F1852E3411}</UniqueIdentifier> + <UniqueIdentifier>{FB7CDB88-6130-4057-81D1-930F6AC33A28}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{793B13DB-7C30-4334-A398-EA998A473206}</UniqueIdentifier> + <UniqueIdentifier>{9D798EBC-3174-4AE0-921F-1AD1C2FC42EE}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{65B4A597-DA92-4875-BC82-E7B93DBCAA49}</UniqueIdentifier> + <UniqueIdentifier>{0DA2AD7A-574E-4412-8DBB-2C95DCFAF0C2}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{37180E6D-5204-4358-98A7-E348F7EBB425}</UniqueIdentifier> + <UniqueIdentifier>{D53692A7-3396-456F-B5EA-DDB999B67D80}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.c b/filesys/miniFilter/swapBuffers/swapBuffers.c index 47893870..8773fde2 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.c +++ b/filesys/miniFilter/swapBuffers/swapBuffers.c @@ -428,8 +428,7 @@ Return Value: // an allocated name buffer. If not, it will be NULL // -#pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "Used to maintain compatability with Win 2k") - status = RtlVolumeDeviceToDosName( devObj, &ctx->Name ); + status = IoVolumeDeviceToDosName( devObj, &ctx->Name ); } // diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.sln b/filesys/miniFilter/swapBuffers/swapBuffers.sln index 88ca2992..bfb94841 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.sln +++ b/filesys/miniFilter/swapBuffers/swapBuffers.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "swapBuffers", "swapBuffers.vcxproj", "{A7B7D32A-A301-4497-A68B-349341047B69}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "swapBuffers", "swapBuffers.vcxproj", "{BF51955F-EC2D-4746-9F6D-A38D203F2AB6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|Win32.ActiveCfg = Debug|Win32 - {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|Win32.Build.0 = Debug|Win32 - {A7B7D32A-A301-4497-A68B-349341047B69}.Release|Win32.ActiveCfg = Release|Win32 - {A7B7D32A-A301-4497-A68B-349341047B69}.Release|Win32.Build.0 = Release|Win32 - {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|x64.ActiveCfg = Debug|x64 - {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|x64.Build.0 = Debug|x64 - {A7B7D32A-A301-4497-A68B-349341047B69}.Release|x64.ActiveCfg = Release|x64 - {A7B7D32A-A301-4497-A68B-349341047B69}.Release|x64.Build.0 = Release|x64 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|Win32.Build.0 = Debug|Win32 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|Win32.ActiveCfg = Release|Win32 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|Win32.Build.0 = Release|Win32 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|x64.ActiveCfg = Debug|x64 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|x64.Build.0 = Debug|x64 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|x64.ActiveCfg = Release|x64 + {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj index 3c7b7151..d3925754 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{A7B7D32A-A301-4497-A68B-349341047B69}</ProjectGuid> + <ProjectGuid>{BF51955F-EC2D-4746-9F6D-A38D203F2AB6}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{3AA28CE9-14ED-49C0-9FC7-A918130F15DA}</SampleGuid> + <SampleGuid>{0B907EAE-2E1A-4640-BF08-A4CB8D10A921}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters index eba4de22..362bb7b1 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{35572264-BD73-4FA0-88AC-7FCC7C0917C7}</UniqueIdentifier> + <UniqueIdentifier>{547C6A8B-43E4-4D52-86E9-F04019916975}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{FF6920BD-0267-4DBD-B420-DED4DD132F5C}</UniqueIdentifier> + <UniqueIdentifier>{43DE78CC-2ACC-400B-9144-EFB61C1A7E2E}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{2BAFD988-E41F-4DB0-A580-19F92CFFE2F4}</UniqueIdentifier> + <UniqueIdentifier>{54FF1AAF-29C0-4B36-A01E-964EC486668A}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{0D9FC8FE-1009-48A7-8A94-DD71E176AEE9}</UniqueIdentifier> + <UniqueIdentifier>{DB21CD94-ADB1-4062-9C3D-0063158FA18F}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> |
