What happens internally when a file path exceeds approx. 32767 characters in Windows?

后端 未结 1 1212
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-30 17:10

In Windows (assume 2000 onwards), a file path can be at most approximately 32767 characters in length. This limitation exists due to the internal handling with UNICODE_STRING in

1条回答
  •  迷失自我
    2021-01-30 17:40

    Because I'm lazy, I didn't write a test program but tested it using the excellent Far Manager which handles things like long paths (longer than MAX_PATH) or special filenames (con, prn etc) just fine.

    I made a string of exactly 255 characters ("12345678901234...012345") and started creating nested directories. Luckily, Far's "Make Directory" function takes a slash-separated string to mean "create nested directories" so I was able to do it in just a few steps by preparing a string in the internal editor with some copy&paste.

    The longest path I was able to create was 32739 characters long, counting from "C:\" (i.e. it does not include "\\?\" added by Far). The error that I get when trying to create a directory or file with just one additional character is "The filename or extension is too long.". If I try to enter that directory, I get the same error.

    EDIT: spent some time in the debugger and here's what happens on the Win32 API level:

    1. I try to create a file with one character above the limit
    2. Far calls CreateFileW with the string "\\?\C:\123[...]012345" which is 32744 wide characters long (not counting the terminating zero).
    3. CreateFileW does some extra checks, converts the null-terminated string to UNICODE_STRING (Length=65488, MaximumLength=65490) and prepares an OBJECT_ATTRIBUTES struct.
    4. CreateFileW then calls NtCreateFile in ntdll.dll, which is just a wrapper around syscall instruction.
    5. NtCreateFile returns 0xC0000106 (STATUS_NAME_TOO_LONG).
    6. That status value is then converted (using RtlNtStatusToDosError) to the Win32 error 206 (ERROR_FILENAME_EXCED_RANGE).

    I did not bother checking what happens in the kernel, but I guess I could have a look at that too.

    EDIT2: I ran WinObj and found that on my system C: is a symlink to \Device\HarddiskVolume1. This string is 23 characters long. If we replace the \C: in the string passed to NtCreateFile with it, we get 32744 - 3 + 23 = 32764 characters. Together with the terminating zero, this requires 65530 bytes. Still short of the limit (0xFFFF=65535) so I guess there's something extra being added, like a session or namespace name.

    EDIT3: after going through the kernel:

    1. NtCreateFile calls IopCreateFile
    2. IopCreateFile calls ObOpenObjectByName
    3. ObOpenObjectByName calls ObpLookupObjectName
    4. ObpLookupObjectName checks for ObpDosDevicesShortNamePrefix ("\??\") -> success
    5. it skips the prefix and splits the remaining part into "C:" and "\1234..."
    6. it resolves the "C:" with a call to ObpLookupDirectoryEntry
    7. it then calls ObpParseSymbolicLink passing to it the looked-up directory entry (_OBJECT_SYMBOLIC_LINK with LinkTarget == "\Device\HarddiskVolume1" and DosDeviceDriveIndex == 3) and the remaining part of the name.
    8. It then does something like this (faithfully reproduced by ReactOS):

      TargetPath = &SymlinkObject->LinkTarget;
      TempLength = TargetPath->Length;
      TotalLength = TempLength + RemainingName->Length;
      if (LengthUsed > 0xFFF0)
          return STATUS_NAME_TOO_LONG;
      

      In our case, 46 + 65476 = 65522 (0xfff2) which is just above the limit.

      So there, mystery solved (I hope!).

    P.S. everything tested under Windows 7 x64 SP1.

    0 讨论(0)
提交回复
热议问题