Что происходит внутри, когда путь к файлу превышает прибл. 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:

  1. я пытаюсь создать файл с одним символом выше предела
  2. Дальний называет CreateFileW в строке "\\?\C:\123[...]012345" которая составляет 32744 широкое символов (не считая завершающего нуля).
  3. CreateFileW выполняет некоторые дополнительные проверки, преобразует null-завершенной строку UNICODE_STRING (Длина=65488, максимальная длина=65490) и готовит OBJECT_ATTRIBUTES структура.
  4. CreateFileW вызывает NtCreateFile на ntdll.dll, который является просто оберткой вокруг syscall инструкция.
  5. NtCreateFile возвращает 0xC0000106 (STATUS_NAME_TOO_LONG).
  6. это значение состояния затем преобразуется (используя RtlNtStatusToDosError) к ошибке Win32 206 (ERROR_FILENAME_EXCED_RANGE).

я не потрудился проверить, что происходит в ядре, но я думаю, что на это тоже можно взглянуть!--76-->.

EDIT2: я побежал WinObj и нашел это в моей системе C: - это симлинк на \Device\HarddiskVolume1. Эта строка 23 символов долго. Если мы заменим \C: в строке передано NtCreateFile С ним мы получаем 32744 - 3 + 23 = 32764 символы. Вместе с завершающим нулем для этого требуется 65530 байт. Все еще не хватает предела (0xFFFF=65535), поэтому я думаю, что есть что-то добавлено дополнительно, например, имя сеанса или пространства имен.

EDIT3: после прохождения ядра:

  1. NtCreateFile звонки IopCreateFile
  2. IopCreateFile звонки ObOpenObjectByName
  3. ObOpenObjectByName звонки ObpLookupObjectName
  4. ObpLookupObjectName проверка ObpDosDevicesShortNamePrefix ("\??\") -> успеха
  5. он пропускает префикс и разбивает оставшуюся часть на "C:" и "34..."
  6. разрешает "C:" С a вызов ObpLookupDirectoryEntry
  7. затем он называет ObpParseSymbolicLink передавая ему поисковую запись каталога (_OBJECT_SYMBOLIC_LINK С LinkTarget =="\Device\HarddiskVolume1" и DosDeviceDriveIndex == 3) и оставшаяся часть имени.
  8. затем он делает что-то вроде этого (точно воспроизводится 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.