Rename of a reparse target

Hi,

I have a file with a custom reparse point on it. It will reparse to another target file. The name resolution seems to work fine. When working with word or other office apps that use the rename to save back the file, the reparse file gets overwritten as well as the reparse point. To avoid this I am intercepting IRP_MJ_SET_INFORMATION for rename and issue the rename to the target but when I complete the operation, driver verifier complains that I cannot complete the IRP since I am not a name provider.

In order to redirect the rename do I really need to be a name provider or is there a better way to handle redirection of a rename?

I am looking at being a name provider to satisfy driver verify. Since I am not trying to virtualize anything, I think just a passthrough name provider may be solve this. From my understanding the minimal name provider would need to implement PFLT_GENERATE_FILE_NAME and
PFLT_NORMALIZE_NAME_COMPONENT. The PFLT_GENERATE_FILE_NAME seems pretty simple to just use FltGetFileNameInformation/FltGetFileNameInformationUnsafe. However I am not understanding what I would need to do in order to implement PFLT_NORMALIZE_NAME_COMPONENT. Is there a way to just send it down to the next filter in the stack?

Thanks for any advice on how to solve this.

Are you using FltSetFileNameInformation and are you a minifilter?
Minifilters must use FSFNI. If this is not your case, you may disregard the post.

Have you looked at SimRep?

@Dejan_Maksimovic, yes I am a minifilter. In order to target the rename to the actual backing target (not the reparse file) I am trying to reissue the io to the backing file. I am currently calling FltSetFileNameInformation from within the IRP_MJ_SET_INFORMATION pre operation to set this on the target file. Then complete the operation to avoid it being sent down and performing the rename on the reparse file.

When I attempted to create a new PFILE_RENAME_INFORMATION and replaced the original buffer in the data->Iopb->Parameters.SetFileInformation.InfoBuffer and let the IO continue there seems to be no effect as the original reparse file still gets renamed. I was able to get the behavior that I wanted when instead I call FltSetFileNameInformation with the new buffer and complete the pre operation but this causes driver verifier to complain that I cannot complete the irp if I am not a name provider (which is more than I want to implement for this).

I hope you meant fltsetinformationfile, that I misspelled also :slight_smile:

How do you call it exactly, can you share the code? I don't recall name filter being a requirement

Thanks @Scott_Noone_OSR I did take a look at simRep and NameChanger as well for the name provider. SimRep seems to be doing what I am already doing by calling FltSetInformationFile with the updated buffer and completing the irp. The only issue with this approach is that driver verifier complains that it is not a name provider when I return FLT_PREOP_COMPLETE. I will keep looking as this does look simpler than the name provider approach.

Yes FltSetInformationFile is what I intended. Here is what I am doing with error checking removed. Excuse any typos as I am unwinding the functions that I am actually calling to make this simpler

if (is_rename_operation(data) && data->Iopb->TargetFileObject) {
  NTSTATUS status;

   PFILE_RENAME_INFORMATION p = (PFILE_RENAME_INFORMATION)data->Iopb->Parameters.SetFileInformation.InfoBuffer;

  UNICODE_STRING file;
  RtlInitUnicodeString(&file, p->FileName);

  // open target file to get the reparse point data
  HANDLE fileHandle;
  PFILE_OBJECT fo;
  OBJECT_ATTRIBUTES objAttr = {NULL};
  IO_STATUS_BLOCK ios;

  InitializeObjectAttributes(&objAttr, file,
                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK,
                               NULL, NULL);
  status = FltCreateFileEx2(filter, data->Iopb->TargetInstance, &handle, &fo, FILE_READ_ATTRIBUTES, &objAttr, &ios, nullptr, 0, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, nullptr, 0, 0, nullptr);
  if (!NT_SUCCESS(status))
   return status;

  UNICODE_STRING targetFile = target_from_reparse_data(data->Iopb->TargetInstance, fo);

  PFILE_RENAME_INFORMATION buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(FILE_RENAME_INFORMATION) + targetFile.Length - sizeof(WCHAR), 'tag');

  buffer->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE;
  buffer->RootDirectory = NULL;
  buffer->FileNameLength = targetFile.Length;
  RtlCopyMemory(buffer->FileName, targetFile.Buffer, targetFile.Length);

  status = FltSetInformationFile(data->Iopb->TargetInstance, data->Iopb->TargetFileObject, buffer, sizeof(FILE_RENAME_INFORMATION) + target->Length - sizeof(WCHAR), FileRenameInformationEx);

  data_->IoStatus.Status = STATUS_SUCCESS;
  return FLT_PREOP_COMPLETE;
}

This seems to be working but after this returns driver verifier breaks.

FILTER VERIFIER ERROR: A filter has completed an operation but does not implement name provider callbacks. For example
if a filter completes an IRP_MJ_CREATE and is not a name provider it is possible that during name
queries the FO will make it's way to the file system and bugcheck. (Filter = FFFFBC0BD8F20050, Cbd = FFFFBC0BE16B4418)

At a quick glance it looks like you are opening the target file and using its FO as the FO (source) in FltSetInformationFile? So renaming it to itself?
Long shot: IoStatus.Information should be set to 0 (not likely to matter here)

Sorry that was my bad when unwinding the actual code to create this sample. I updated the code above. I am not renaming onto the same file but from the data->Iopb->TargetFileObject. I am still seeing this issue after setting IoStatus.Information to 0 explicitly. Looking in debugger it was already 0 so this should not really impact anything.

This code has the right behavior but the verifier error is causing problems for me. Is this something that I can ignore or is there a real issue here? I hate to turn off verifier but not sure other than becoming a name provider how to work around this.

Looking at the stack it clearly looks like FLTMGR!FltpvVerifyPreOperationStatus is not happy with this since I am not a name provider.

3: kd> kn
 # Child-SP          RetAddr               Call Site
00 fffffd05`1e8c2180 fffff803`6109f34f     FLTMGR!FltpvPrintErrors+0x1a5
01 fffffd05`1e8c2400 fffff803`610a18fc     FLTMGR!FltpvVerifyPreOperationStatus+0x143
02 fffffd05`1e8c2450 fffff803`6102c7a1     FLTMGR!FltvPreOperation+0x2cc
03 fffffd05`1e8c2570 fffff803`6102be31     FLTMGR!FltpPerformPreCallbacksWorker+0x631
04 fffffd05`1e8c2700 fffff803`6102ad88     FLTMGR!FltpPassThroughInternal+0xf1
05 fffffd05`1e8c27b0 fffff803`6102a93d     FLTMGR!FltpPassThrough+0x218
06 fffffd05`1e8c28a0 fffff803`cf9cfbec     FLTMGR!FltpDispatch+0xed
07 fffffd05`1e8c2940 fffff803`d027ead4     nt!IopfCallDriver+0x8c
08 fffffd05`1e8c2980 fffff803`cf9cffcc     nt!IovCallDriver+0x44
09 fffffd05`1e8c29c0 fffff803`cf9cfd16     nt!IofCallDriver+0xac
0a fffffd05`1e8c2a00 fffff803`cfabd19d     nt!IopCallDriverReference+0xe6
0b fffffd05`1e8c2a80 fffff803`cfd9ef55     nt!NtSetInformationFile+0x83d
0c fffffd05`1e8c2bb0 00007ffe`d347c4a4     nt!KiSystemServiceCopyEnd+0x25
0d 0000009b`003f99e8 00007ffe`b7171740     ntdll!NtSetInformationFile+0x14
0e 0000009b`003f99f0 00007ffe`d0cc945f     AppVIsvSubsystems64!vfs_hooks::hooked_NtSetInformationFile+0x90
0f 0000009b`003f9a30 00007ffe`d0cc87e7     KERNELBASE!ReplaceFileExInternal+0xc3f
10 0000009b`003f9f10 00007ffe`9d8ab1c2     KERNELBASE!ReplaceFileW+0x27
11 0000009b`003f9f60 00007ffe`64d6a8ed     mso20win32client!MsoReplaceFileW+0x1ca
12 0000009b`003fa2e0 00007ffe`9fcfcf88     mso30win32client!Mso::EnterpriseDataProtection::FileProtectionManagerCore::ReplaceFileW+0x11d
13 0000009b`003fa3d0 00007ffe`9f84bd2b     wwlib!EnterpriseDataProtection::FReplaceFile+0x3c
14 0000009b`003fa410 00007ffe`9f84b138     wwlib!FReplaceFn+0xa4b
15 0000009b`003fafc0 00007ffe`a00504a4     wwlib!FExchangeOrReplaceBackupFn+0xe0
16 0000009b`003fb030 00007ffe`9f55c054     wwlib!FReplaceBackupFn+0x244
17 0000009b`003fb3a0 00007ffe`9f55c722     wwlib!IOLDocTransaction::HrReplaceSavedFile+0x274
18 0000009b`003fb6a0 00007ffe`9f55c4eb     wwlib!IOLDocLocalSaveTransaction::HrCommit+0x1f2
19 0000009b`003fb930 00007ffe`9f52a43b     wwlib!PersisterIMsoOLDoc::HrCommitTransaction+0x11b
1a 0000009b`003fb9d0 00000269`00000000     wwlib!EidSaveFile+0x384b
1b 0000009b`003fb9d8 000039d6`4b237868     0x00000269`00000000
1c 0000009b`003fb9e0 00000000`0228c4d5     0x000039d6`4b237868
1d 0000009b`003fb9e8 00640002`01010101     0x228c4d5
1e 0000009b`003fb9f0 0000009b`003fbe90     0x00640002`01010101
1f 0000009b`003fb9f8 0000009b`ffffffff     0x0000009b`003fbe90
20 0000009b`003fba00 0000009b`00000000     0x0000009b`ffffffff
21 0000009b`003fba08 0000009b`00000000     0x0000009b`00000000
22 0000009b`003fba10 0000009b`003fba54     0x0000009b`00000000
23 0000009b`003fba18 00000000`00000000     0x0000009b`003fba54

As for implementing a name provider I have been looking at the NameChanger sample. I can return the reparse target instead of the reparse file but for other files I am not sure how to handle them. I think I should just be able to callFltGetFileNameInformation/FltGetFileNameInformationUnsafe. Is this the right approach?

It might really be needed for renaming via FltSetInformationFile. I cannot recall, I did work a lot on this but 4+ years ago.

Yes you can implement the pass-through model. Which I think must have a better way (e.g. return STATUS_NOT_SUPPORTED) to not call needlessly into name queries.. try that first.

But ye, NameChanger (almost identical to above code) is the basic.

Thank @Dejan_Maksimovic for your help here this seems to be working now.