Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTFSD

Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Case sensitivity crossing reparse points

Jorgen_LundmanJorgen_Lundman Member - All Emails Posts: 49
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 | <[email protected]>
Unix Administrator | +81 (0)90-5578-8500
Shibuya-ku, Tokyo | Japan

Comments

  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,590
    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

    -scott
    OSR

  • Jorgen_LundmanJorgen_Lundman Member - All Emails Posts: 49
    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 :)

    Lund

    [email protected] 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://www.osr.com/seminars&gt;
    >
    > To unsubscribe, visit the List Server section of OSR Online at
    > <http://www.osronline.com/page.cfm?name=ListServer&gt;
    >

    --
    Jorgen Lundman | <[email protected]>
    Unix Administrator | +81 (0)90-5578-8500
    Shibuya-ku, Tokyo | Japan
  • Malcolm_SmithMalcolm_Smith Member Posts: 124
    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 :)
    >
    > Lund
    >
    > [email protected] 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://www.osr.com/seminars&gt;
    >>
    >> To unsubscribe, visit the List Server section of OSR Online at
    >> <http://www.osronline.com/page.cfm?name=ListServer&gt;
    >>
    >


    --
    http://www.malsmith.net
  • rbmmrbmm Member Posts: 10
    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
  • Jorgen_LundmanJorgen_Lundman Member - All Emails Posts: 49
    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


    [email protected] 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://www.osr.com/seminars&gt;
    >
    > To unsubscribe, visit the List Server section of OSR Online at <http://www.osronline.com/page.cfm?name=ListServer&gt;
    >

    --
    Jorgen Lundman | <[email protected]>
    Unix Administrator | +81 (0)90-5578-8500
    Shibuya-ku, Tokyo | Japan
  • rbmmrbmm Member Posts: 10
    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.
  • Craig_BarkhouseCraig_Barkhouse Member - All Emails Posts: 19
    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: [email protected] [mailto:[email protected]] On Behalf Of Jorgen Lundman <[email protected]>
    Sent: Sunday, February 4, 2018 3:49 PM
    To: Windows File Systems Devs Interest List <[email protected]>
    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


    [email protected] 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://i.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://paste
    > 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://i.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://paste
    > 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://i.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://paste
    > 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
    > 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
    > 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 | <[email protected]>
    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://na01.safelinks.protection.outlook.com/?url=http://www.osr.com/seminars&amp;data=02|01|[email protected]|a13f04df754d4d70006908d56c29f3c8|ee3303d7fb734b0c8589bcd847f1c277|1|0|636533849802536315&amp;sdata=ZhlqIYZUiXgDEdLYtLNjVQwdLEWBXfOyO+Z7yJp4Mgc=&amp;reserved=0&gt;

    To unsubscribe, visit the List Server section of OSR Online at <https://na01.safelinks.protection.outlook.com/?url=http://www.osronline.com/page.cfm?name=ListServer&amp;data=02|01|[email protected]|a13f04df754d4d70006908d56c29f3c8|ee3303d7fb734b0c8589bcd847f1c277|1|0|636533849802536315&amp;sdata=xvEuE0OkCs2RKYPO7L3iP0lLpGyV6GEtbS9jQoSV1nA=&amp;reserved=0&gt;
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Internals & Software Drivers 19-23 June 2023 Live, Online
Writing WDF Drivers 10-14 July 2023 Live, Online
Kernel Debugging 16-20 October 2023 Live, Online
Developing Minifilters 13-17 November 2023 Live, Online