Что происходит внутри, когда путь к файлу превышает прибл. 32767 символов в Windows?
в Windows (предположим, 2000 и далее) путь к файлу может быть не более 32767 символов в длину. Это ограничение существует из-за внутренней обработки с UNICODE_STRING
в собственном API (также на стороне ядра, в драйверах и т. д.). Пока все хорошо. Я знаю теорию, стоящую за этой частью.
причина ограничения заключается в том, что Length
и MaximumLength
члены UNICODE_STRING
подсчитайте количество байтов в Buffer
, но являются 16 битными целыми числами без знака сами себя.
Я также знаю, почему предел является приближением, а не установленным пределом. Это в основном связано с тем, как имя файла (например,.C:boot.ini
) решается в своей родной форме (например,??C:boot.ini
), а затем к чему-то, что имеет префикс фактический имя устройства тома, а затем следуют пути относительно этого тома, например DeviceHarddiskVolume2boot.ini
.
кроме того, из Проводника Windows известный симптом при нажатии ("ANSI")MAX_PATH
лимит притворяться файл или папка не существует в некоторых версиях Windows (возможно, это было исправлено в какой-то момент).
но что происходит на уровне менеджера объектов, менеджера ввода-вывода и драйвера файловой системы соответственно когда я называю CreateFile()
С путем, который выглядит как .C:...filename.ext
и всю дорогу не превысить предел, но достигает это, в моем призыве к kernel32.dll
' s CreateFile()
и затем расширяется? ...
ни SDKs и WDKs, похоже, особенно разговорчивы по этой теме. Или я заглянул не в те разделы?
1 ответов
потому что я ленивый, я не написал тестовую программу, но протестировал ее с помощью excellent Far Manager который обрабатывает такие вещи, как длинные (длиннее, чем MAX_PATH
) или специальные имена файлов (con
, prn
etc) просто отлично.
я сделал строку ровно из 255 символов ("12345678901234...012345") и начал создавать вложенные каталоги. К счастью, функция Far "Make Directory" принимает строку, разделенную косой чертой, чтобы означать "создание вложенных каталогов", поэтому я смог сделать это всего за несколько шагов, подготовив строку во внутреннем редакторе с некоторой копией и вставкой.
самый длинный путь, который я смог создать, был 32739 символы длинные, считая от "C:\" (т. е. он не включает"\\?\ "добавлено далеко). Ошибка, которую я получаю при попытке создать каталог или файл с одним дополнительным символом, -"имя файла или расширение слишком длинное.". Если я попытаюсь войти в этот каталог, я получу то же самое ошибка.
редактировать: провел некоторое время в отладчике, и вот что происходит на уровне Win32 API:
- я пытаюсь создать файл с одним символом выше предела
- Дальний называет
CreateFileW
в строке "\\?\C:\123[...]012345" которая составляет 32744 широкое символов (не считая завершающего нуля). -
CreateFileW
выполняет некоторые дополнительные проверки, преобразует null-завершенной строкуUNICODE_STRING
(Длина=65488, максимальная длина=65490) и готовитOBJECT_ATTRIBUTES
структура. -
CreateFileW
вызываетNtCreateFile
наntdll.dll
, который является просто оберткой вокругsyscall
инструкция. -
NtCreateFile
возвращает 0xC0000106 (STATUS_NAME_TOO_LONG
). - это значение состояния затем преобразуется (используя
RtlNtStatusToDosError
) к ошибке Win32 206 (ERROR_FILENAME_EXCED_RANGE
).
я не потрудился проверить, что происходит в ядре, но я думаю, что на это тоже можно взглянуть!--76-->.
EDIT2: я побежал WinObj и нашел это в моей системе C:
- это симлинк на \Device\HarddiskVolume1
. Эта строка 23 символов долго. Если мы заменим \C:
в строке передано NtCreateFile
С ним мы получаем 32744 - 3 + 23 = 32764 символы. Вместе с завершающим нулем для этого требуется 65530 байт. Все еще не хватает предела (0xFFFF=65535), поэтому я думаю, что есть что-то добавлено дополнительно, например, имя сеанса или пространства имен.
EDIT3: после прохождения ядра:
-
NtCreateFile
звонкиIopCreateFile
-
IopCreateFile
звонкиObOpenObjectByName
-
ObOpenObjectByName
звонкиObpLookupObjectName
-
ObpLookupObjectName
проверкаObpDosDevicesShortNamePrefix
("\??\"
) -> успеха - он пропускает префикс и разбивает оставшуюся часть на
"C:"
и"34..."
- разрешает
"C:"
С a вызовObpLookupDirectoryEntry
- затем он называет
ObpParseSymbolicLink
передавая ему поисковую запись каталога (_OBJECT_SYMBOLIC_LINK
СLinkTarget
=="\Device\HarddiskVolume1"
иDosDeviceDriveIndex
== 3) и оставшаяся часть имени. -
затем он делает что-то вроде этого (точно воспроизводится ReactOS):
TargetPath = &SymlinkObject->LinkTarget; TempLength = TargetPath->Length; TotalLength = TempLength + RemainingName->Length; if (LengthUsed > 0xFFF0) return STATUS_NAME_TOO_LONG;
в нашем случае 46 + 65476 = 65522 (0xfff2), что чуть выше предела.
Итак, тайна решена (I Надежда!).
П. С. все проверил под Windows 7 x64 с пакетом обновления 1.