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 callsNtCreateFile
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.