问题
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 the native API (also on the kernel side, in drivers etc). So far so good. I know the theory behind that part.
The reason for the limit is that the the Length
and MaximumLength
members of UNICODE_STRING
count the number of bytes in the Buffer
, but are 16 bit unsigned integers themselves.
I also know why the limit is an approximation rather than a set limit. This is mostly due to how your file name (e.g. \\.\C:\boot.ini
) gets resolved to its native form (e.g. \??\C:\boot.ini
) and then to something that is prefixed by the actual volume device name and then followed by the path relative to that volume, e.g. \Device\HarddiskVolume2\boot.ini
.
Furthermore from Windows Explorer the known symptom when hitting the ("ANSI") MAX_PATH
limit is to pretend the file or folder doesn't exist in some versions of Windows (possible this got fixed at some point).
But what happens at the object manager, I/O manager and file system driver levels respectively when I call CreateFile()
with a path that looks like \\.\C:\...\filename.ext
and the whole path does not exceed the limit, but reaches it, in my call to kernel32.dll
's CreateFile()
and then gets expanded? ...
Neither the SDKs nor the WDKs seem to be particularly chatty about the topic. Or did I look in the wrong sections?
回答1:
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:
- I try to create a file with one character above the limit
- Far calls
CreateFileW
with the string "\\?\C:\123[...]012345" which is 32744 wide characters long (not counting the terminating zero). CreateFileW
does some extra checks, converts the null-terminated string toUNICODE_STRING
(Length=65488, MaximumLength=65490) and prepares anOBJECT_ATTRIBUTES
struct.CreateFileW
then calls NtCreateFile inntdll.dll
, which is just a wrapper aroundsyscall
instruction.NtCreateFile
returns 0xC0000106 (STATUS_NAME_TOO_LONG
).- 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:
NtCreateFile
callsIopCreateFile
IopCreateFile
callsObOpenObjectByName
ObOpenObjectByName
callsObpLookupObjectName
ObpLookupObjectName
checks forObpDosDevicesShortNamePrefix
("\??\"
) -> success- it skips the prefix and splits the remaining part into
"C:"
and"\1234..."
- it resolves the
"C:"
with a call toObpLookupDirectoryEntry
- it then calls
ObpParseSymbolicLink
passing to it the looked-up directory entry (_OBJECT_SYMBOLIC_LINK
withLinkTarget
=="\Device\HarddiskVolume1"
andDosDeviceDriveIndex
== 3) and the remaining part of the name. 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.
来源:https://stackoverflow.com/questions/15262110/what-happens-internally-when-a-file-path-exceeds-approx-32767-characters-in-win