Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 204
0xC0000022L Avatar asked Mar 07 '13 02:03

0xC0000022L


People also ask

What is the maximum path length limitation in Windows?

In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters. A local path is structured in the following order: drive letter, colon, backslash, name components separated by backslashes, and a terminating null character.

What is the maximum number of characters allowed in a filename path?

Individual components of a filename (i.e. each subdirectory along the path, and the final filename) are limited to 255 characters, and the total path length is limited to approximately 32,000 characters. However, on Windows, you can't exceed MAX_PATH value (259 characters for files, 248 for folders).

Why does Windows have a path length limit?

The 260-character path limit is due to fixed buffers in the runtime library for DOS/Windows path processing (e.g. ANSI <=> Unicode, working directory, and DOS<=>NT path conversion).


1 Answers

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.

like image 135
Igor Skochinsky Avatar answered Oct 05 '22 08:10

Igor Skochinsky