32

在 Windows(假设 2000 以后)中,文件路径的长度最多约为 32767 个字符。由于本机 API 中的内部处理UNICODE_STRING(也在内核端、驱动程序等中),存在此限制。到现在为止还挺好。我知道那部分背后的理论。

限制的原因是Length和的MaximumLength成员UNICODE_STRING计算 中的字节数Buffer,但它们本身是 16 位无符号整数。

我也知道为什么限制是近似值而不是设定限制。这主要是由于您的文件名(例如\\.\C:\boot.ini)如何解析为其本机形式(例如\??\C:\boot.ini),然后解析为以实际卷设备名称为前缀的内容,然后是相对于该卷的路径,例如\Device\HarddiskVolume2\boot.ini.

此外,在 Windows 资源管理器中,达到(“ANSI”)MAX_PATH限制时的已知症状是假装文件或文件夹在某些版本的 Windows 中不存在(可能在某些时候得到修复)。

但是,当我CreateFile()使用看起来像的路径进行调用\\.\C:\...\filename.ext并且整个路径没有超过限制但达到限制时,分别在对象管理器、I/O 管理器和文件系统驱动程序级别会发生什么,在我对kernel32.dll's 的调用中CreateFile()然后得到扩大?...

SDK 和 WDK 似乎都没有特别谈论这个话题。还是我看错了部分?

4

1 回答 1

39

因为我很懒,所以我没有编写测试程序,而是使用出色的Far Manager对其进行了测试,该管理器可以处理长路径(长于MAX_PATH)或特殊文件名(conprn)。

我创建了一个正好 255 个字符的字符串(“12345678901234...012345”)并开始创建嵌套目录。幸运的是,Far 的“制作目录”函数采用斜杠分隔的字符串来表示“创建嵌套目录”,因此我只需几个步骤即可通过在内部编辑器中准备一个字符串并进行一些复制和粘贴来完成它。

我能够创建的最长路径是32739 个字符,从“C:\”开始计算(即不包括 Far 添加的“\\?\”)。尝试创建仅包含一个附加字符的目录或文件时出现的错误是“文件名或扩展名太长。 ”。如果我尝试进入该目录,我会得到同样的错误。

编辑:在调试器中花了一些时间,这是在 Win32 API 级别上发生的事情:

  1. 我尝试创建一个字符超过限制的文件
  2. CreateFileW使用字符串 "\\?\C:\123[...]012345" 进行远调用,该字符串长32744 个宽字符(不包括终止零)。
  3. CreateFileW做一些额外的检查,将 null 终止的字符串转换为UNICODE_STRING(Length=65488, MaximumLength=65490) 并准备一个OBJECT_ATTRIBUTES结构。
  4. CreateFileW然后调用NtCreateFilein ntdll.dll,这只是syscall指令的包装。
  5. NtCreateFile返回0xC0000106 ( STATUS_NAME_TOO_LONG)。
  6. 然后将该状态值转换(使用RtlNtStatusToDosError)为 Win32 错误 206 ( ERROR_FILENAME_EXCED_RANGE)。

我没有费心检查内核中发生了什么,但我想我也可以看看

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:""\1234..."
  6. "C:"通过调用来解决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) 刚好高于限制。

    所以在那里,谜团解开了(我希望!)。

PS 在 Windows 7 x64 SP1 下测试的所有内容。

于 2013-03-12T22:49:10.237 回答