Case sensitivity crossing reparse points

Hello list,

Thanks to earlier assistance, I can now mount my filesystem as a “named
mount” as opposed to a drive letter.

C:\POOL

Where "C:" is standard NTFS OS drive, and “POOL” is the reparse point for
my filesystem.

Inside the “POOL” directory there is a “HelloWorld” directory. I can see
this directory fine, however in attempting to enter it I get failures.

It appears to try to IRP_MJ_CREATE on “HELLOWORLD”, which will fail as my
filesystem is case-sensitive.

When my filesystem has a drive letter everything works fine, so it feels like…

… because C: is case-insensitive, it fails to notice the reparsepoint
target is now case-sensitive. Does it not handle changes in capability when
crossing reparse-points, or am I not setting it clearly enough somewhere.

Is that the case? Is there any way around it?

I claim sensitivity with:

FILE_FS_ATTRIBUTE_INFORMATION *ffai = Irp->AssociatedIrp.SystemBuffer;
ffai->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES |
FILE_CASE_SENSITIVE_SEARCH | FILE_NAMED_STREAMS |
FILE_PERSISTENT_ACLS | /*FILE_SUPPORTS_OBJECT_IDS |*/
FILE_SUPPORTS_SPARSE_FILES | FILE_VOLUME_QUOTAS |
FILE_SUPPORTS_REPARSE_POINTS;

Lund


Jorgen Lundman |
Unix Administrator | +81 (0)90-5578-8500
Shibuya-ku, Tokyo | Japan

I keep trying to think of a good response to this, but in reality you?re probably one of very few (if not the first) people to try to mount a case sensitive file system as a reparse point in Windows. If Explorer isn?t handling this properly then you?re probably just out of luck. I can?t think of any reason why it would arbitrarily upcase the name of the folder, but I also don?t really understand anything the shell does on Windows (or any other OS, for that matter)…

Presumably you can get to this properly via the command prompt?

-scott
OSR
@OSRDrivers

Yeah I might be doing something new. I did notice that IRP_MJ_CREATE could
maybe be called without SL_CASE_SENSITIVE - so maybe I can at least detect
when this would happen, and pass it down to internal name comparison routines.

Or just tell ZFS users on Windows to use casesensitivity=insensitive.

For now I’ll put it on TODO, most people will probably mount ZFS as its own
drive letter, and everything works normally then.

Next big challenge is mapping Unix UID to Windows SIDs (or whatever it
uses). But first I’ll have to go read about it and see if I can figure it
out :slight_smile:

Lund

xxxxx@osr.com wrote:

I keep trying to think of a good response to this, but in reality you?re
probably one of very few (if not the first) people to try to mount a
case sensitive file system as a reparse point in Windows. If Explorer
isn?t handling this properly then you?re probably just out of luck. I
can?t think of any reason why it would arbitrarily upcase the name of
the folder, but I also don?t really understand anything the shell does
on Windows (or any other OS, for that matter)…

Presumably you can get to this properly via the command prompt?

-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:
>


Jorgen Lundman |
Unix Administrator | +81 (0)90-5578-8500
Shibuya-ku, Tokyo | Japan</http:></http:>

Case sensitivity on Windows is a bit of a mess. The first result when I
searched for FILE_CASE_SENSITIVE_SEARCH was:

http://www.nicklowe.org/2012/02/understanding-case-sensitivity-in-windows-obcaseinsensitive-file_case_sensitive_search/

Which seems like good background reading.

I guess I’d say though that there’s three levels of case insensitivity:

  1. The system can be forced to cause all toplevel creates to be case
    insensitive via the obcaseinsensitive regkey. (Note there is no inverse
    that forces all to be case sensitive.)
  2. The file system/volume can indicate that it will process all creates
    as case insensitive via the absence of FILE_CASE_SENSITIVE_SEARCH.
    (Note there is no inverse that indicates the volume will process all
    opens case sensitively.)
  3. The thing opening a file can indicate that it wants case sensitive
    semantics if (1) and (2) allow it to. (Note there is no inverse to
    insist that all creates will be case sensitive.)

There’s plenty of code in Windows/Win32/the application ecosystem that
assumes case insensitivity. Interpreting opens case sensitively when
that behavior was not requested is a flat out bug that will break
things. It will break things in the most subtle ways too, like causing
fltmgr to incorrectly assume two names resolve to the same name cache
entry, and start confusing other filters operating in the stack.

Since (1) is set to force case insensitivity by default, it’s highly
unlikely that any create is set to case sensitive, unless it’s coming
from the Linux subsystem, which is special.

In the specific case here, things are even worse because the create is
firstly issued to the volume containing the reparse point and there’s no
way to indicate that the create should be case insensitive up to the
reparse point and case sensitive thereafter. But I think the real issue
here is that the create was always a case insensitive create, and that
means anything in the stack is entitled to change case.

  • M

On 01/24/2018 07:12 PM, Jorgen Lundman wrote:

Yeah I might be doing something new. I did notice that IRP_MJ_CREATE could
maybe be called without SL_CASE_SENSITIVE - so maybe I can at least detect
when this would happen, and pass it down to internal name comparison routines.

Or just tell ZFS users on Windows to use casesensitivity=insensitive.

For now I’ll put it on TODO, most people will probably mount ZFS as its own
drive letter, and everything works normally then.

Next big challenge is mapping Unix UID to Windows SIDs (or whatever it
uses). But first I’ll have to go read about it and see if I can figure it
out :slight_smile:

Lund

xxxxx@osr.com wrote:
> I keep trying to think of a good response to this, but in reality you?re
> probably one of very few (if not the first) people to try to mount a
> case sensitive file system as a reparse point in Windows. If Explorer
> isn?t handling this properly then you?re probably just out of luck. I
> can?t think of any reason why it would arbitrarily upcase the name of
> the folder, but I also don?t really understand anything the shell does
> on Windows (or any other OS, for that matter)…
>
> Presumably you can get to this properly via the command prompt?
>
> -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://www.malsmith.net</http:></http:>

Is there any way around it? yes, but undocumented and only in case IO_REPARSE_TAG_MOUNT_POINT (but not in case IO_REPARSE_TAG_SYMLINK)

first of all - why this happens ? this is done by NTFS in NtfsFindStartingNode procedure
NTFS uppercase file name during parsing in NtfsFindStartingNode for calculate hash
which than used in NtfsFindPrefixHashEntry. also if PrefixHashEntry found - NTFS revert file name component back,
by call memmove from NtfsFindPrefixHashEntry. so NTFS usual temporary uppercase file name and than revert it to original form
but in case reparse point found - uppercase whole name, but revert back only until reparse name.
part after reparse file name left in uppercase

in case say \Pool\HelloWorld\XxXx NTFS first convert name to \POOL\HELLOWORLD\XXXX, then revert back to \Pool\HELLOWORLD\XXXX
and return STATUS_REPARSE with IO_REPARSE_TAG_MOUNT_POINT in your case and return
Tail.Overlay.AuxiliaryBuffer will point to REPARSE_DATA_BUFFER
assume that in REPARSE_DATA_BUFFER will be \Pool -> \MyDevice[\SomePath]
based on this info IopParseDevice transorm file name and new Irp will be sendto \MyDevice device
new file name will be [\SomePath]\HELLOWORLD\XXXX

before win7 (or vista - i not check here) when you view request to [\SomePath]\HELLOWORLD\XXXX
you can not even know - are user direct open
\MyDevice[\SomePath]\HELLOWORLD\XXXX
or open via reparse
??\c:\Pool\HELLOWORLD\XXXX

but after extra create parameter (ECP) appeared, situation is changed.

begin how minimum win7 (i not check on vista) special IopSymlinkECPGuid exist in kernel:

struct __declspec(uuid(“73d5118a-88ba-439f-92f4-46d38952d250”)) IopSymlinkECPGuid;

and IopParseDevice attach ECP context of type IopSymlinkECPGuid to Irp after reparse.
so now we can know exactly, are create request was via reparse - need check for IopSymlinkECPGuid

PECP_LIST EcpList;
SYMLINK_ECP_CONTEXT* EcpContext;

if (0 <= FsRtlGetEcpListFromIrp(Irp, &EcpList) && EcpList &&
0 <= FsRtlFindExtraCreateParameter(EcpList, &__uuidof(IopSymlinkECPGuid), (void**)&EcpContext, 0)){

//…
}

the context point to next structure:

struct SYMLINK_ECP_CONTEXT
{
USHORT UnparsedNameLength;
union {
USHORT Flags;
struct {
USHORT MountPoint : 1;
};
};
USHORT DeviceNameLength;
USHORT Zero;
SYMLINK_ECP_CONTEXT* Reparsed;
UNICODE_STRING Name;
};

the Flags i not exactly research -
in case IO_REPARSE_TAG_MOUNT_POINT (a00000003) low bit is set to 1, Flags == 3
in case IO_REPARSE_TAG_SYMLINK (a000000c) Flags == 2
also exist special case for undocumented reparse tag 80000019

the Name always containing full path (including device name) to file

Zero always init to 0 (padding field)

DeviceNameLength containing length in bytes of device path in Name

in case IO_REPARSE_TAG_MOUNT_POINT:
Name containing original file path from open request
UnparsedNameLength containing length in bytes of unparsed suffix (path after mount pount component)
Reparsed point to linked SYMLINK_ECP_CONTEXT data with
Flags = 0, Name containing reparsed file path

in case IO_REPARSE_TAG_SYMLINK the UnparsedNameLength and Reparsed is always 0
and Name containing reparsed file path

so in case IO_REPARSE_TAG_SYMLINK SYMLINK_ECP_CONTEXT containing much less info compare IO_REPARSE_TAG_MOUNT_POINT.

let concrete example:

on \Device\HarddiskVolume2 exist mount point :

\Pool -> \MyDevice[\SomePath]

and somebody try open file \Device\HarddiskVolume2\Pool\HelloWorld\XxXx

----- Irp #1 to \Device\HarddiskVolume2 --------

FileName = \Pool\HelloWorld\XxXx
no IopSymlinkECPGuid context

NTFS transform FileName to \Pool\HELLOWORLD\XXXX and return

(STATUS_REPARSE, IO_REPARSE_TAG_MOUNT_POINT)

----- Irp #2 to \MyDevice --------

FileName = [\SomePath]\HELLOWORLD\XXXX
exist IopSymlinkECPGuid context (Flags = 3):

Name = \Device\HarddiskVolume2\Pool\HelloWorld\XxXx
DeviceNameLength = size of L"\Device\HarddiskVolume2" (not including terminated 0)
UnparsedNameLength = sizeof of L"\HelloWorld\XxXx" (not including terminated 0)
Reparsed -> IopSymlinkECPGuid context (Flags = 0):
Name = \MyDevice[\SomePath]\HELLOWORLD\XXXX
DeviceNameLength = size of L"\MyDevice"
UnparsedNameLength = 0
Reparsed = 0

so based on this info you can got original unparsed file name (in original case) - \HelloWorld\XxXx
and modify file name from [\SomePath]\HELLOWORLD\XXXX to \HelloWorld\XxXx

BOOLEAN RevertFileName(PIRP Irp)
{
PECP_LIST EcpList;
SYMLINK_ECP_CONTEXT* EcpContext;

if (0 <= FsRtlGetEcpListFromIrp(Irp, &EcpList) &&
EcpList &&
0 <= FsRtlFindExtraCreateParameter(EcpList, &__uuidof(IopSymlinkECPGuid), (void**)&EcpContext, 0) &&
!FsRtlIsEcpFromUserMode(EcpContext) &&
EcpContext->MountPoint
)
{
if (USHORT UnparsedNameLength = EcpContext->UnparsedNameLength)
{
PUNICODE_STRING FileName = &IoGetCurrentIrpStackLocation(Irp)->FileObject->FileName;
USHORT FileNameLength = FileName->Length;
USHORT NameLength = EcpContext->Name.Length;

if (UnparsedNameLength <= NameLength && UnparsedNameLength <= FileNameLength)
{
UNICODE_STRING us1 = {
UnparsedNameLength,
UnparsedNameLength,
(PWSTR)RtlOffsetToPointer(FileName->Buffer, FileNameLength - UnparsedNameLength)
};

UNICODE_STRING us2 = {
UnparsedNameLength,
UnparsedNameLength,
(PWSTR)RtlOffsetToPointer(EcpContext->Name.Buffer, NameLength - UnparsedNameLength)
};

if (RtlEqualUnicodeString(&us1, &us2, TRUE))
{
memcpy(us1.Buffer, us2.Buffer, UnparsedNameLength);

return TRUE;
}
}
}
}

return FALSE;
}

some logs:
https://i.imgur.com/fUUFRWM.png https://pastebin.com/nRpZrZtJ
https://i.imgur.com/W3ywGUw.png https://pastebin.com/kCvzMRyi
https://i.imgur.com/2k0dARs.png https://pastebin.com/geHM893X

Thanks, this is quite interesting and it is good to know the cause, and a
way to work around it. I especially appreciate the “concrete example” at end.

Lund

xxxxx@gmail.com wrote:

Is there any way around it? yes, but undocumented and only in case IO_REPARSE_TAG_MOUNT_POINT (but not in case IO_REPARSE_TAG_SYMLINK)

first of all - why this happens ? this is done by NTFS in NtfsFindStartingNode procedure
NTFS uppercase file name during parsing in NtfsFindStartingNode for calculate hash
which than used in NtfsFindPrefixHashEntry. also if PrefixHashEntry found - NTFS revert file name component back,
by call memmove from NtfsFindPrefixHashEntry. so NTFS usual temporary uppercase file name and than revert it to original form
but in case reparse point found - uppercase whole name, but revert back only until reparse name.
part after reparse file name left in uppercase

in case say \Pool\HelloWorld\XxXx NTFS first convert name to \POOL\HELLOWORLD\XXXX, then revert back to \Pool\HELLOWORLD\XXXX
and return STATUS_REPARSE with IO_REPARSE_TAG_MOUNT_POINT in your case and return
Tail.Overlay.AuxiliaryBuffer will point to REPARSE_DATA_BUFFER
assume that in REPARSE_DATA_BUFFER will be \Pool -> \MyDevice[\SomePath]
based on this info IopParseDevice transorm file name and new Irp will be sendto \MyDevice device
new file name will be [\SomePath]\HELLOWORLD\XXXX

before win7 (or vista - i not check here) when you view request to [\SomePath]\HELLOWORLD\XXXX
you can not even know - are user direct open
\MyDevice[\SomePath]\HELLOWORLD\XXXX
or open via reparse
??\c:\Pool\HELLOWORLD\XXXX

but after extra create parameter (ECP) appeared, situation is changed.

begin how minimum win7 (i not check on vista) special IopSymlinkECPGuid exist in kernel:

struct __declspec(uuid(“73d5118a-88ba-439f-92f4-46d38952d250”)) IopSymlinkECPGuid;

and IopParseDevice attach ECP context of type IopSymlinkECPGuid to Irp after reparse.
so now we can know exactly, are create request was via reparse - need check for IopSymlinkECPGuid

PECP_LIST EcpList;
SYMLINK_ECP_CONTEXT* EcpContext;

if (0 <= FsRtlGetEcpListFromIrp(Irp, &EcpList) && EcpList &&
0 <= FsRtlFindExtraCreateParameter(EcpList, &__uuidof(IopSymlinkECPGuid), (void**)&EcpContext, 0)){

//…
}

the context point to next structure:

struct SYMLINK_ECP_CONTEXT
{
USHORT UnparsedNameLength;
union {
USHORT Flags;
struct {
USHORT MountPoint : 1;
};
};
USHORT DeviceNameLength;
USHORT Zero;
SYMLINK_ECP_CONTEXT* Reparsed;
UNICODE_STRING Name;
};

the Flags i not exactly research -
in case IO_REPARSE_TAG_MOUNT_POINT (a00000003) low bit is set to 1, Flags == 3
in case IO_REPARSE_TAG_SYMLINK (a000000c) Flags == 2
also exist special case for undocumented reparse tag 80000019

the Name always containing full path (including device name) to file

Zero always init to 0 (padding field)

DeviceNameLength containing length in bytes of device path in Name

in case IO_REPARSE_TAG_MOUNT_POINT:
Name containing original file path from open request
UnparsedNameLength containing length in bytes of unparsed suffix (path after mount pount component)
Reparsed point to linked SYMLINK_ECP_CONTEXT data with
Flags = 0, Name containing reparsed file path

in case IO_REPARSE_TAG_SYMLINK the UnparsedNameLength and Reparsed is always 0
and Name containing reparsed file path

so in case IO_REPARSE_TAG_SYMLINK SYMLINK_ECP_CONTEXT containing much less info compare IO_REPARSE_TAG_MOUNT_POINT.

let concrete example:

on \Device\HarddiskVolume2 exist mount point :

\Pool -> \MyDevice[\SomePath]

and somebody try open file \Device\HarddiskVolume2\Pool\HelloWorld\XxXx

----- Irp #1 to \Device\HarddiskVolume2 --------

FileName = \Pool\HelloWorld\XxXx
no IopSymlinkECPGuid context

NTFS transform FileName to \Pool\HELLOWORLD\XXXX and return

(STATUS_REPARSE, IO_REPARSE_TAG_MOUNT_POINT)

----- Irp #2 to \MyDevice --------

FileName = [\SomePath]\HELLOWORLD\XXXX
exist IopSymlinkECPGuid context (Flags = 3):

Name = \Device\HarddiskVolume2\Pool\HelloWorld\XxXx
DeviceNameLength = size of L"\Device\HarddiskVolume2" (not including terminated 0)
UnparsedNameLength = sizeof of L"\HelloWorld\XxXx" (not including terminated 0)
Reparsed -> IopSymlinkECPGuid context (Flags = 0):
Name = \MyDevice[\SomePath]\HELLOWORLD\XXXX
DeviceNameLength = size of L"\MyDevice"
UnparsedNameLength = 0
Reparsed = 0

so based on this info you can got original unparsed file name (in original case) - \HelloWorld\XxXx
and modify file name from [\SomePath]\HELLOWORLD\XXXX to \HelloWorld\XxXx

BOOLEAN RevertFileName(PIRP Irp)
{
PECP_LIST EcpList;
SYMLINK_ECP_CONTEXT* EcpContext;

if (0 <= FsRtlGetEcpListFromIrp(Irp, &EcpList) &&
EcpList &&
0 <= FsRtlFindExtraCreateParameter(EcpList, &__uuidof(IopSymlinkECPGuid), (void**)&EcpContext, 0) &&
!FsRtlIsEcpFromUserMode(EcpContext) &&
EcpContext->MountPoint
)
{
if (USHORT UnparsedNameLength = EcpContext->UnparsedNameLength)
{
PUNICODE_STRING FileName = &IoGetCurrentIrpStackLocation(Irp)->FileObject->FileName;
USHORT FileNameLength = FileName->Length;
USHORT NameLength = EcpContext->Name.Length;

if (UnparsedNameLength <= NameLength && UnparsedNameLength <= FileNameLength)
{
UNICODE_STRING us1 = {
UnparsedNameLength,
UnparsedNameLength,
(PWSTR)RtlOffsetToPointer(FileName->Buffer, FileNameLength - UnparsedNameLength)
};

UNICODE_STRING us2 = {
UnparsedNameLength,
UnparsedNameLength,
(PWSTR)RtlOffsetToPointer(EcpContext->Name.Buffer, NameLength - UnparsedNameLength)
};

if (RtlEqualUnicodeString(&us1, &us2, TRUE))
{
memcpy(us1.Buffer, us2.Buffer, UnparsedNameLength);

return TRUE;
}
}
}
}

return FALSE;
}

some logs:
https://i.imgur.com/fUUFRWM.png https://pastebin.com/nRpZrZtJ
https://i.imgur.com/W3ywGUw.png https://pastebin.com/kCvzMRyi
https://i.imgur.com/2k0dARs.png https://pastebin.com/geHM893X


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:
>


Jorgen Lundman |
Unix Administrator | +81 (0)90-5578-8500
Shibuya-ku, Tokyo | Japan</http:></http:>

i use RevertFileName from minifilter-precreate (with of course replace FsRtlGetEcpListFromIrp to FltGetEcpListFromCallbackData) and it always (in my test on win7,8,10) correct revert name case to original after mount point reparse.

For what it’s worth, in the upcoming Windows release (1803? not sure what it will be called), NTFS now correctly preserves the case of the unparsed path.

Craig [MSFT]

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of Jorgen Lundman
Sent: Sunday, February 4, 2018 3:49 PM
To: Windows File Systems Devs Interest List
Subject: Re: [ntfsd] Case sensitivity crossing reparse points

Thanks, this is quite interesting and it is good to know the cause, and a way to work around it. I especially appreciate the “concrete example” at end.

Lund

xxxxx@gmail.com wrote:
> Is there any way around it? yes, but undocumented and only in case
> IO_REPARSE_TAG_MOUNT_POINT (but not in case IO_REPARSE_TAG_SYMLINK)
>
> first of all - why this happens ? this is done by NTFS in
> NtfsFindStartingNode procedure NTFS uppercase file name during parsing
> in NtfsFindStartingNode for calculate hash which than used in
> NtfsFindPrefixHashEntry. also if PrefixHashEntry found - NTFS revert
> file name component back, by call memmove from NtfsFindPrefixHashEntry. so NTFS usual temporary uppercase file name and than revert it to original form but in case reparse point found - uppercase whole name, but revert back only until reparse name.
> part after reparse file name left in uppercase
>
> in case say \Pool\HelloWorld\XxXx NTFS first convert name to
> \POOL\HELLOWORLD\XXXX, then revert back to \Pool\HELLOWORLD\XXXX and
> return STATUS_REPARSE with IO_REPARSE_TAG_MOUNT_POINT in your case and
> return Tail.Overlay.AuxiliaryBuffer will point to REPARSE_DATA_BUFFER
> assume that in REPARSE_DATA_BUFFER will be \Pool ->
> \MyDevice[\SomePath] based on this info IopParseDevice transorm file
> name and new Irp will be sendto \MyDevice device new file name will be
> [\SomePath]\HELLOWORLD\XXXX
>
> before win7 (or vista - i not check here) when you view request to
> [\SomePath]\HELLOWORLD\XXXX you can not even know - are user direct
> open \MyDevice[\SomePath]\HELLOWORLD\XXXX
> or open via reparse
> ??\c:\Pool\HELLOWORLD\XXXX
>
> but after extra create parameter (ECP) appeared, situation is changed.
>
> begin how minimum win7 (i not check on vista) special IopSymlinkECPGuid exist in kernel:
>
> struct __declspec(uuid(“73d5118a-88ba-439f-92f4-46d38952d250”))
> IopSymlinkECPGuid;
>
> and IopParseDevice attach ECP context of type IopSymlinkECPGuid to Irp after reparse.
> so now we can know exactly, are create request was via reparse - need
> check for IopSymlinkECPGuid
>
> PECP_LIST EcpList;
> SYMLINK_ECP_CONTEXT* EcpContext;
>
> if (0 <= FsRtlGetEcpListFromIrp(Irp, &EcpList) && EcpList &&
> 0 <= FsRtlFindExtraCreateParameter(EcpList,
> &__uuidof(IopSymlinkECPGuid), (void**)&EcpContext, 0)){
>
> //…
> }
>
> the context point to next structure:
>
> struct SYMLINK_ECP_CONTEXT
> {
> USHORT UnparsedNameLength;
> union {
> USHORT Flags;
> struct {
> USHORT MountPoint : 1;
> };
> };
> USHORT DeviceNameLength;
> USHORT Zero;
> SYMLINK_ECP_CONTEXT* Reparsed;
> UNICODE_STRING Name;
> };
>
> the Flags i not exactly research -
> in case IO_REPARSE_TAG_MOUNT_POINT (a00000003) low bit is set to 1,
> Flags == 3 in case IO_REPARSE_TAG_SYMLINK (a000000c) Flags == 2 also
> exist special case for undocumented reparse tag 80000019
>
> the Name always containing full path (including device name) to file
>
> Zero always init to 0 (padding field)
>
> DeviceNameLength containing length in bytes of device path in Name
>
> in case IO_REPARSE_TAG_MOUNT_POINT:
> Name containing original file path from open request
> UnparsedNameLength containing length in bytes of unparsed suffix (path
> after mount pount component) Reparsed point to linked SYMLINK_ECP_CONTEXT data with
> Flags = 0, Name containing reparsed file path
>
> in case IO_REPARSE_TAG_SYMLINK the UnparsedNameLength and Reparsed is
> always 0 and Name containing reparsed file path
>
> so in case IO_REPARSE_TAG_SYMLINK SYMLINK_ECP_CONTEXT containing much less info compare IO_REPARSE_TAG_MOUNT_POINT.
>
> let concrete example:
>
> on \Device\HarddiskVolume2 exist mount point :
>
> \Pool -> \MyDevice[\SomePath]
>
> and somebody try open file
> \Device\HarddiskVolume2\Pool\HelloWorld\XxXx
>
> ----- Irp #1 to \Device\HarddiskVolume2 --------
>
> FileName = \Pool\HelloWorld\XxXx
> no IopSymlinkECPGuid context
>
> NTFS transform FileName to \Pool\HELLOWORLD\XXXX and return
>
> (STATUS_REPARSE, IO_REPARSE_TAG_MOUNT_POINT)
>
> ----- Irp #2 to \MyDevice --------
>
> FileName = [\SomePath]\HELLOWORLD\XXXX exist IopSymlinkECPGuid context
> (Flags = 3):
>
> Name = \Device\HarddiskVolume2\Pool\HelloWorld\XxXx
> DeviceNameLength = size of L"\Device\HarddiskVolume2" (not including
> terminated 0) UnparsedNameLength = sizeof of L"\HelloWorld\XxXx" (not
> including terminated 0) Reparsed -> IopSymlinkECPGuid context (Flags = 0):
> Name = \MyDevice[\SomePath]\HELLOWORLD\XXXX
> DeviceNameLength = size of L"\MyDevice"
> UnparsedNameLength = 0
> Reparsed = 0
>
> so based on this info you can got original unparsed file name (in
> original case) - \HelloWorld\XxXx and modify file name from
> [\SomePath]\HELLOWORLD\XXXX to \HelloWorld\XxXx
>
> BOOLEAN RevertFileName(PIRP Irp)
> {
> PECP_LIST EcpList;
> SYMLINK_ECP_CONTEXT* EcpContext;
>
> if (0 <= FsRtlGetEcpListFromIrp(Irp, &EcpList) &&
> EcpList &&
> 0 <= FsRtlFindExtraCreateParameter(EcpList, &__uuidof(IopSymlinkECPGuid), (void**)&EcpContext, 0) &&
> !FsRtlIsEcpFromUserMode(EcpContext) &&
> EcpContext->MountPoint
> )
> {
> if (USHORT UnparsedNameLength = EcpContext->UnparsedNameLength)
> {
> PUNICODE_STRING FileName = &IoGetCurrentIrpStackLocation(Irp)->FileObject->FileName;
> USHORT FileNameLength = FileName->Length;
> USHORT NameLength = EcpContext->Name.Length;
>
> if (UnparsedNameLength <= NameLength && UnparsedNameLength <= FileNameLength)
> {
> UNICODE_STRING us1 = {
> UnparsedNameLength,
> UnparsedNameLength,
> (PWSTR)RtlOffsetToPointer(FileName->Buffer, FileNameLength - UnparsedNameLength)
> };
>
> UNICODE_STRING us2 = {
> UnparsedNameLength,
> UnparsedNameLength,
> (PWSTR)RtlOffsetToPointer(EcpContext->Name.Buffer, NameLength - UnparsedNameLength)
> };
>
> if (RtlEqualUnicodeString(&us1, &us2, TRUE))
> {
> memcpy(us1.Buffer, us2.Buffer, UnparsedNameLength);
>
> return TRUE;
> }
> }
> }
> }
>
> return FALSE;
> }
>
> some logs:
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fi.img
> ur.com%2FfUUFRWM.png&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df
> 754d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C
> 636533849802526307&sdata=9z5SNgIe6qB47uxjSy6cDBatM8l3Etd8a4tqdII90%2F4
> %3D&reserved=0
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpaste
> bin.com%2FnRpZrZtJ&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df75
> 4d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C63
> 6533849802526307&sdata=rCSNHNa1py%2Bmci3viCgiLXGOFoqDuoZrWa29wk0l%2FCw
> %3D&reserved=0
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fi.img
> ur.com%2FW3ywGUw.png&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df
> 754d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C
> 636533849802536315&sdata=Z%2B8vMxfVz0DriU%2BJwPaNQxNat2%2FxTVsHdPgyT0%
> 2F7880%3D&reserved=0
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpaste
> bin.com%2FkCvzMRyi&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df75
> 4d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C63
> 6533849802536315&sdata=yunEmYl4dODe7rtkNw%2Fh05AELIPiEJ6z9RQ%2Blhmc39Q
> %3D&reserved=0
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fi.img
> ur.com%2F2k0dARs.png&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df
> 754d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C
> 636533849802536315&sdata=9JncSZMoRFshzR3SWOr1RscdJKemv%2B1Mj1OxYh9tpAY
> %3D&reserved=0
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpaste
> bin.com%2FgeHM893X&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df75
> 4d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C63
> 6533849802536315&sdata=PftwONtVlH%2BJOyStjTaXuY86m2Oe6dmvcb1bCdrbJNM%3
> D&reserved=0
>
>
> —
> NTFSD is sponsored by OSR
>
>
> MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers!
> Details at
> https:> sr.com%2Fseminars&data=02%7C01%7Ccraigba%40microsoft.com%7Ca13f04df754
> d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589bcd847f1c277%7C1%7C0%7C636
> 533849802536315&sdata=ZhlqIYZUiXgDEdLYtLNjVQwdLEWBXfOyO%2BZ7yJp4Mgc%3D
> &reserved=0>
>
> To unsubscribe, visit the List Server section of OSR Online at
> https:> sronline.com%2Fpage.cfm%3Fname%3DListServer&data=02%7C01%7Ccraigba%40m
> icrosoft.com%7Ca13f04df754d4d70006908d56c29f3c8%7Cee3303d7fb734b0c8589
> bcd847f1c277%7C1%7C0%7C636533849802536315&sdata=xvEuE0OkCs2RKYPO7L3iP0
> lLpGyV6GEtbS9jQoSV1nA%3D&reserved=0>
>


Jorgen Lundman |
Unix Administrator | +81 (0)90-5578-8500
Shibuya-ku, Tokyo | Japan


NTFSD is sponsored by OSR

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

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