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
CreateFileWwith the string “\\?\C:\123[…]012345”
which is 32744 wide characters long (not counting the terminating zero). CreateFileWdoes some extra checks, converts the null-terminated string toUNICODE_STRING(Length=65488, MaximumLength=65490) and prepares anOBJECT_ATTRIBUTESstruct.CreateFileWthen callsNtCreateFileinntdll.dll, which is just a wrapper aroundsyscallinstruction.NtCreateFilereturns 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:
NtCreateFilecallsIopCreateFileIopCreateFilecallsObOpenObjectByNameObOpenObjectByNamecallsObpLookupObjectNameObpLookupObjectNamechecks 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
ObpParseSymbolicLinkpassing to it the looked-up directory entry (_OBJECT_SYMBOLIC_LINKwithLinkTarget=="\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.