Redirector + Word

A more readable version: https://dev.nlited.com/p2201.htm

I have created a redirector driver to provide an interface (\nurl) to files
on a remote system. It is working, with one notable exception that has me
flummoxed: Saving a file using Office Word.

The driver is a full driver implementation (what Microsoft now calls a
“legacy” driver) to leverage a lot of existing framework code from other
projects. I see every IRP in its original state. This is not a filter driver
since there is no lower device to filter. I have FASTIO stubs, but they all
return FALSE.

The remote file system will be very basic, definitely not NTFS. The current
prototype stores the remote files in a subdirectory on the same local
volume, so in this version the “remote” file system happens to be NTFS. This
lets me pull NTFS information for the files to test theories during
development, but this will not be possible in the final version.

All this is working. I can create, write, save, open, read, and update
remote files using console commands (copy, type, etc). I can create, update,
and save files using notepad.exe. I can list directories.

I can open and read existing files using Word. Saving files using Word
fails. Word successfully saves the updated contents to a temporary file,
then fails to replace the original file with the updated temporary file. In
fact, Word does not even try to rename the files. I don’t know why.

I can use Word to save a file on a “normal” network share and I see the
following sequence: (Normal Log)

  1. Create a temporary file.

  2. Write the contents to the temporary file.

  3. Set the saved file’s LastWrite time.

  4. Copy the security attributes.

  5. Copy the file creation attribute from the original file to the
    temporary file.

  6. Query the volume attributes.

  7. Update the saved file’s security attributes. (again).

  8. Copy the NTFS OBJECT_ID.

  9. Use IRP_MJ_SET_INFORMATION:FileRename to rename the original file to
    a temporary name.

  10. Use IRP_MJ_SET_INFORMATION:FileRename to rename the saved file to
    the original name.

  11. Write a NTFS USN_CLOSE record to the saved file.

  12. Update the saved file’s security attributes. (again).

  13. Delete the original file (now with a new temporary name) using
    IRP_MJ_SET_INFORMATION:FileDisposition=DELETE.

=============

When I save the file through my driver, I see the following: (nurl log)

  1. Create a temporary file.

  2. Write the contents to the temporary file.

  3. Query FileRemoteProtocolInformation

NOT_IMPLEMENTED

  1. Try to open a different temporary file.

The file does not exist, OBJECT_NAME_NOT_FOUND:DOES_NOT_EXIST.

  1. Query the original security information.

The security info is returned as null (not empty), indicating no
security. This is intended, since the ultimate remote file system will not
support security descriptors. I have tried returning the actual security
descriptor from the NTFS file, with no effect.

There is no attempt to set the security info on the temporary file.

This is expected since the original file has no security info. I do not
believe this is the culprit, but I can’t be 100% certain.

  1. Copy the file creation attribute.

This version does not actually update the attribute. I don’t see any
further queries so I don’t believe this is a problem.

  1. Query the volume attributes.

The remote volume supports a minimal feature set: no ObjectID, named
streams, ACL, USN, etc.

Word seems to be ignoring my volume attributes and trying to update
ObjectID, security info, and USN records anyway.

  1. Query the original file’s stream information

This is unexpected, since my volume doesn’t support streams.

I have tried returning NOT_IMPLEMENTED and valid stream information.
Neither made any difference.

  1. Delete the saved file’s OBJECT_ID and query the original file’s
    OBJECT_ID.

I return NOT_SUPPORTED for both. There is no attempt to set the saved
file’s OBJECT_ID.

  1. Query the original file’s FileNameInformation.

I return “\nurl\MyServer\Test.txt” (single leading backslash as per
docs)

  1. Query the original file’s Basic info.

  2. Try to open a second temporary file (same as before)

The file still does not exist, OBJECT_NAME_NOT_FOUND:DOES_NOT_EXIST

  1. Write a USN_CLOSE record to the saved file.

I believe Word has already deemed the saved file as unacceptable at this
point. I expect to see the renaming operations before the USN_CLOSE.
However, I don’t really expect to see the USN operation at all since my
volume doesn’t support it. I return NOT_SUPPORTED.

  1. Query the saved file’s attribute information.

  2. Delete the saved file using
    IRP_MJ_SET_INFORMATION:Disposition=DELETE.

I don’t actually delete the file, but do return SUCCESS.

The saved file contains the complete updated contents. I can open and read
it using Word, and there doesn’t seem to be any problem with it. But for
some reason, Word refuses to accept it and tells the user that the save
failed: “There has been a network or file permission error. The network
connection may be lost. (\nurl\MyServer\Test.txt)”.

Does anyone know what it takes to make Word happy?

There are tons of things Word does which are not so obvious. The one thing
which does stand out in your listing below is that you say you don’t
actually delete the renamed, original file. I’m betting there is a directory
enumeration or an attempt to open that same file shortly after the delete?
If you are still reporting the file exists, Word may think something went
awry and will complain.

If the data is present and good, I suspect one of the edge cases like this
which is not making Word happy.

Pete

Kernel Drivers

Windows File System and Device Driver Consulting

https:ta=02%7C01%7C%7C264d56615a8e4e5ba1a708d5b5c536b9%7C63545f2732324d74a44dcdd45
7063402%7C1%7C0%7C636614780992839365&sdata=TjZJ2NVU2pmPJZKorNXepS%2BkCCdw27B
6%2BHlgnJCuybk%3D&reserved=0> www.KernelDrivers.com

866.263.9295

From: xxxxx@lists.osr.com
On Behalf Of
Sent: Saturday, July 14, 2018 1:41 PM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Redirector + Word

A more readable version: https://dev.nlited.com/p2201.htm

I have created a redirector driver to provide an interface (\nurl
<file:> ) to files on a remote system. It is working, with one notable
exception that has me flummoxed: Saving a file using Office Word.

The driver is a full driver implementation (what Microsoft now calls a
“legacy” driver) to leverage a lot of existing framework code from other
projects. I see every IRP in its original state. This is not a filter driver
since there is no lower device to filter. I have FASTIO stubs, but they all
return FALSE.

The remote file system will be very basic, definitely not NTFS. The current
prototype stores the remote files in a subdirectory on the same local
volume, so in this version the “remote” file system happens to be NTFS. This
lets me pull NTFS information for the files to test theories during
development, but this will not be possible in the final version.

All this is working. I can create, write, save, open, read, and update
remote files using console commands (copy, type, etc). I can create, update,
and save files using notepad.exe. I can list directories.

I can open and read existing files using Word. Saving files using Word
fails. Word successfully saves the updated contents to a temporary file,
then fails to replace the original file with the updated temporary file. In
fact, Word does not even try to rename the files. I don’t know why.

I can use Word to save a file on a “normal” network share and I see the
following sequence: (Normal Log)

1. Create a temporary file.

2. Write the contents to the temporary file.

3. Set the saved file’s LastWrite time.

4. Copy the security attributes.

5. Copy the file creation attribute from the original file to the
temporary file.

6. Query the volume attributes.

7. Update the saved file’s security attributes. (again).

8. Copy the NTFS OBJECT_ID.

9. Use IRP_MJ_SET_INFORMATION:FileRename to rename the original file to
a temporary name.

10. Use IRP_MJ_SET_INFORMATION:FileRename to rename the saved file to
the original name.

11. Write a NTFS USN_CLOSE record to the saved file.

12. Update the saved file’s security attributes. (again).

13. Delete the original file (now with a new temporary name) using
IRP_MJ_SET_INFORMATION:FileDisposition=DELETE.

=============

When I save the file through my driver, I see the following: (nurl log)

1. Create a temporary file.

2. Write the contents to the temporary file.

3. Query FileRemoteProtocolInformation

NOT_IMPLEMENTED

4. Try to open a different temporary file.

The file does not exist, OBJECT_NAME_NOT_FOUND:DOES_NOT_EXIST.

5. Query the original security information.

The security info is returned as null (not empty), indicating no
security. This is intended, since the ultimate remote file system will not
support security descriptors. I have tried returning the actual security
descriptor from the NTFS file, with no effect.

There is no attempt to set the security info on the temporary file.

This is expected since the original file has no security info. I do not
believe this is the culprit, but I can’t be 100% certain.

6. Copy the file creation attribute.

This version does not actually update the attribute. I don’t see any
further queries so I don’t believe this is a problem.

7. Query the volume attributes.

The remote volume supports a minimal feature set: no ObjectID, named
streams, ACL, USN, etc.

Word seems to be ignoring my volume attributes and trying to update
ObjectID, security info, and USN records anyway.

8. Query the original file’s stream information

This is unexpected, since my volume doesn’t support streams.

I have tried returning NOT_IMPLEMENTED and valid stream information.
Neither made any difference.

9. Delete the saved file’s OBJECT_ID and query the original file’s
OBJECT_ID.

I return NOT_SUPPORTED for both. There is no attempt to set the saved
file’s OBJECT_ID.

10. Query the original file’s FileNameInformation.

I return “\nurl\MyServer\Test.txt” (single leading backslash as per
docs)

11. Query the original file’s Basic info.

12. Try to open a second temporary file (same as before)

The file still does not exist, OBJECT_NAME_NOT_FOUND:DOES_NOT_EXIST

13. Write a USN_CLOSE record to the saved file.

I believe Word has already deemed the saved file as unacceptable at this
point. I expect to see the renaming operations before the USN_CLOSE.
However, I don’t really expect to see the USN operation at all since my
volume doesn’t support it. I return NOT_SUPPORTED.

14. Query the saved file’s attribute information.

15. Delete the saved file using
IRP_MJ_SET_INFORMATION:Disposition=DELETE.

I don’t actually delete the file, but do return SUCCESS.

The saved file contains the complete updated contents. I can open and read
it using Word, and there doesn’t seem to be any problem with it. But for
some reason, Word refuses to accept it and tells the user that the save
failed: “There has been a network or file permission error. The network
connection may be lost. (\nurl\MyServer\Test.txt
<file:> )”.

Does anyone know what it takes to make Word happy?


NTFSD is sponsored by OSR

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></file:></file:></https:>

TL;DR: Do I need to create or attach a device other than the device I give
to FsRtlRegisterUncProviderEx() ?

I don’t think my non-deletion is the problem because I never see any
directory enumerations after that point. HOWEVER, this relies on a big
assumption.

I register my redirector device using FsRtlRegisterUncProviderEx(“\nurl”).
This is the only device that I expect to ever receive any file IO requests
for any path that begins with \nurl.

I am *assuming* the only way the system can access anything under my claimed
redirection path (\nurl\ <file:></file:> ) involves sending an IRP to this
device.

HOWEVER. when I run ProcMon I see a CREATE(\nurl\MyServer\ , WRITE_DATA)
request that is not present in my internal logs. ProcMon says this extra
request is failing with NAME NOT FOUND (which I am assuming is actually
C0000034:STATUS_OBJECT_NAME_NOT_FOUND). This appears to happen just before
Word gives up on the save operation, so this may be the culprit. This
surprises me because there are several earlier CREATE(\nurl\MyServer\ ,
READ_DATA) requests that I am handling successfully, but my logs do not
contain the WRITE_DATA request. I am assuming the WRITE_DATA is extracted
from IO_STACK_LOCATION->Parameters.Create.SecurityContext->DesiredAccess.

So my new question: Is this IRP somehow bypassing my redirector device? Do I
need to create (or attach to) a file system device that is also receiving
requests for my path?

TL;DR: Do I need to create or attach a device other than the device I give
to FsRtlRegisterUncProviderEx() ?

I don’t think my non-deletion is the problem because I never see any
directory enumerations after that point. HOWEVER, this relies on a big
assumption.

I register my redirector device using FsRtlRegisterUncProviderEx(“\nurl”).
This is the only device that I expect to ever receive any file IO requests
for any path that begins with \nurl.

I am *assuming* the only way the system can access anything under my claimed
redirection path (file://nurl/) involves sending an IRP to this device.

HOWEVER. when I run ProcMon I see a CREATE(\nurl\MyServer\ , WRITE_DATA)
request that is not present in my internal logs. ProcMon says this extra
request is failing with NAME NOT FOUND (which I am assuming is actually
C0000034:STATUS_OBJECT_NAME_NOT_FOUND). This appears to happen just before
Word gives up on the save operation, so this may be the culprit. This
surprises me because there are several earlier CREATE(\nurl\MyServer\ ,
READ_DATA) requests that I am handling successfully, but my logs do not
contain the WRITE_DATA request. I am assuming the WRITE_DATA is extracted
from IO_STACK_LOCATION->Parameters.Create.SecurityContext->DesiredAccess.

So my new question: Is this IRP somehow bypassing my redirector device? Do I
need to create (or attach to) a file system device that is also receiving
requests for my path?

Are you handling the IOCTL_REDIR_QUERY_PATH(_EX) calls from MUP? This is how
MUP determines which device to send paths to for processing. And MUP will
only cache the results for a short time.

Pete

Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
866.263.9295

-----Original Message-----
From: xxxxx@lists.osr.com
On Behalf Of
Sent: Monday, July 16, 2018 11:42 PM
To: Windows File Systems Devs Interest List
Subject: RE: [ntfsd] Redirector + Word

TL;DR: Do I need to create or attach a device other than the device I give
to FsRtlRegisterUncProviderEx() ?

I don’t think my non-deletion is the problem because I never see any
directory enumerations after that point. HOWEVER, this relies on a big
assumption.

I register my redirector device using FsRtlRegisterUncProviderEx(“\nurl”).
This is the only device that I expect to ever receive any file IO requests
for any path that begins with \nurl.

I am assuming the only way the system can access anything under my claimed
redirection path (file://nurl/) involves sending an IRP to this device.

HOWEVER. when I run ProcMon I see a CREATE(\nurl\MyServer\ , WRITE_DATA)
request that is not present in my internal logs. ProcMon says this extra
request is failing with NAME NOT FOUND (which I am assuming is actually
C0000034:STATUS_OBJECT_NAME_NOT_FOUND). This appears to happen just before
Word gives up on the save operation, so this may be the culprit. This
surprises me because there are several earlier CREATE(\nurl\MyServer\ ,
READ_DATA) requests that I am handling successfully, but my logs do not
contain the WRITE_DATA request. I am assuming the WRITE_DATA is extracted
from IO_STACK_LOCATION->Parameters.Create.SecurityContext->DesiredAccess.

So my new question: Is this IRP somehow bypassing my redirector device? Do I
need to create (or attach to) a file system device that is also receiving
requests for my path?


NTFSD is sponsored by OSR

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:>

I spent the day comparing my internal IRP log to the ProcMon log. It seems like something (MUP?) is handling some (not all) of the CREATE requests for my redirector. This seems benign until one of the CreateFile operations fails with NAME NOT FOUND without calling my redirector. I have no record in my logs of this failed request. I don’t understand what is going on.

Evidence:

* The ProcMon log is filtered by process (WORD.EXE) and path (\nurl).

* The ProcMon log contains only CreateFile (and FastIO QueryCreate) operations, nothing else. I would expect to see at least the matching CleanUp and Close operations. All the CreateFile operations (except the final failure) succeed or fail as expected.

* My driver registers itself by calling FsRtlRegisterUncProviderEx(). This is the only interface for the redirector device.

* My redirector device receives a single QUERY_PATH_EX request, and it affirmatively claims the \nurl prefix. It then seems to receive all the expected requests.

* My log contains many Create, CleanUp, Close, query, read, write, and other operations. All behave as expected.

* Word behaves normally until the failed CreateFile(\nurl\MyServer) operation. This includes reading the original file and saving to a new temporary file.

* There are many CREATE(\nurl\MyServer) operations that succeed.

* Each CreateFile in ProcMon is followed by a CREATE in IrpLog a few milliseconds later. Sometimes they overlap, but they are not (never?) nested. I am using KeQuerySystemTimePrecise() for my timestamps and I am *assuming* ProcMon is using this as well.

* The failed CreateFile(\nurl\MyServer) always happens at the same point, just before I expect Word to rename the original and temporary files. This ProcMon CreateFile does *not* have a matching CREATE entry in my log.

* The failed CreateFile(\nurl\MyServer) is the only request to include WRITE access.

To clarify the last point:

* The failed CreateFile(\nurl\MyServer) is the only CreateFile(\nurl\MyServer) request to include WRITE access.

(Back from vacation and trying to get caught up…)

Super weird. Did you ever get any more data from this? Did you try getting a
network trace to see if the request was going out to some other redirector?

-scott
OSR
@OSRDrivers

I have instrumented the 'ell out of my driver, and the story just gets
stranger…

Let me emphasize that *everything is working fine all the way through saving
the document to a temporary file*. Then MUP returns a an error contrary to
my driver.

I can run ProcMon on top of my driver and I see that each CreateFile()
reported by ProcMon results in a CREATE irp arriving at my redirector’s
device a millisecond (roughly) later. I process the create and the result
reported by ProcMon matches my returned result.

UNTIL… There is a CreateFile(“\nurl\MyServer”) with WRITE access that
results in a “NAME NOT FOUND” reported by ProcMon.

CreateFile NAME NOT FOUND \nurl\MyServer\
Desired Access: Write Data/Add File, Synchronize, Disposition: Open,
Options: , Attributes: n/a, ShareMode: Read, Write, AllocationSize: n/a

The matching CREATE irp sent to my device does *not* have WRITE access and I
returned SUCCESS (just like the hundreds of identical, problem-free create’s
before this):

0:CREATE[0] 0:SUCCESS [1] \nurl\MyServer\
1:OPEN 1:OPENED
Opt[1004021:|DIRECTORY_FILE|SYNCHRONOUS_IO_NONALERT|OPEN_FOR_BACKUP_INTENT]
Attr[80:|NORMAL] Name Type
SecurityContext:
FullOptions[4021:|DIRECTORY_FILE|SYNCHRONOUS_IO_NONALERT|OPEN_FOR_BACKUP_INT
ENT] Access[100001:|READ_DATA|SYNC] ACCESS_STATE AuxData Flags[103]
RemainingDesiredAccess[0:] PreviouslyGrantedAccess[100001:|READ_DATA|SYNC]
OriginalDesiredAccess[100001:|READ_DATA|SYNC]
SubjectSecurityContext[ImpersonationLevel[0:Anon] PrimaryToken
ProcessAuditId]

The CREATE is immediately followed by CLEANUP, CLOSE.

This happens after the document has been saved to a temporary file and Word
is about to rename the files to replace the original. From the perspective
of my driver, there seems to be no problem, no error. But Word gets an error
and aborts the save operation. It seems like MUP is making a decision based
on something other than my SUCCESS result. Also, why I am not seeing the
WRITE bit? I am looking at
IO_STACK_LOCATION.Parameters.Create.SecurityContext->DesiredAccess.

So I am wondering if there is some other external interface that is required
to make this work. Or possibly something buried in the FILE_OBJECT? Am I
looking in the wrong place for the WRITE bit?

The only interface I am creating is through FsRtlRegisterUncProviderEx(),
then responding to REDIR_QUERY_PATH_EX and all the following irps. I do not
create any volume devices (although I do create a VPB for the FILE_OBJECTs).

-----Original Message-----
From: xxxxx@lists.osr.com
On Behalf Of Scott Noone

Sent: Wednesday, July 25, 2018 8:58 AM
To: Windows File Systems Devs Interest List
Subject: Re:[ntfsd] Redirector + Word

(Back from vacation and trying to get caught up…)

Super weird. Did you ever get any more data from this? Did you try getting a
network trace to see if the request was going out to some other redirector?

-scott
OSR
@OSRDrivers


NTFSD is sponsored by OSR

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:>

I’m really curious about this case…Sorry that I haven’t had more to share.
I have literally looked at this at least once a day in the hopes of giving
you a magic bullet. Sorry to say I’ve used all my magic elsewhere this week
:slight_smile:

But, here’s what I can say thus far:

  1. IrpSp->Parameters.Create.SecurityContext->DesiredAccess is the correct
    place to look for the desired access

  2. FsRtlRegisterUncProviderEx/REDIR_QUERY_PATH_EX is the right thing to do,
    shouldn’t need anything else.

  3. You don’t want a VPB in your file objects, those are for local media.
    What are you pointing the VPB device objects to? I can’t imagine that would
    be the problem (unless the mystery open is a relative one?), but it’s
    definitely not correct

In terms of where I would go from here…I’d want a break on the CreateFile
request so I could step it through MUP. Yes, this will be a slog, but the
“name not found” has to come from SOMEwhere. Maybe this is being passed to
another redirector for some reason? Or is MUP returning it internally?

Here’s another thought: if you generate an EXACT version of this create
request using FileTest, does it reach your redirector?

It’s an odd status because I’d expect “bad network name” or “bad network
path” if this was handed to the wrong redirector. MUP also doesn’t appear to
return this status value in any normal paths. Using my trick for tracking
where a status comes from
(https://www.osr.com/blog/2017/07/14/tracking-ntstatus-source/), I only see
a few cases:

kd> lm mmup
Browse full module list
start end module name
8ade1000 8adf1000 mup (pdb symbols)

kd> s -d 8ade1000 8adf1000 C0000034
8adeb940 c0000034 5e5fc78b 0008c25d 90909090 4…_^]…

kd> ln 8adeb940
(8adeb90f) mup!MupiGetProviderIdFromName+0x31

kd> s -d 8ade1001 8adf1000 C0000034
8adeb835 c0000034 000084e9 f04d8b00 480c458b 4…M…E.H

kd> ln 8adeb835
(8adeb753) mup!MupiGetProviderInfoFromFileObject+0xe2

kd> s -d 8ade1002 8adf1000 C0000034
8adeb7be c0000034 000139e9 58488b00 3bf04d89 4…9…HX.M.;

kd> ln 8adeb7be
(8adeb753) mup!MupiGetProviderInfoFromFileObject+0x6b

kd> s -d 8ade1003 8adf1000 C0000034
8ade8fd3 c0000034 458d3d75 ff5350f8 de40b815 4…u=.E.PS…@.

kd> ln 8ade8fd3
(8ade8f13) mup!MupiCreateProviderSymbolicLink+0xc0

Given this, you might want to put breakpoints in MupiGetProviderIdFromName
and MupiGetProviderInfoFromFileObject to see if they’re throwing this error
for some reason. Seems VERY unlikely that the error is coming from any of
these, but it’s something to try while coming up with something better to
try…

Also fighting mysterious Office problems this week, so I feel your pain…

-scott
OSR
@OSRDrivers