当文件路径超过大约时,内部会发生什么.Windows中32767个字符?

0xC*_*22L 32 windows winapi internals

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

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

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

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

但是,在对象管理器,I/O管理器和文件系统驱动程序级别分别发生时,我打电话CreateFile()与看起来像一个路径\\.\C:\...\filename.ext和整个路径并没有超过限制,但达到它,我呼吁kernel32.dllCreateFile(),并随后得到扩大呢?...

SDK和WDK似乎都没有特别讨论这个话题.还是我看错了?

Igo*_*sky 38

因为我很懒,我没有写一个测试程序,但使用优秀测试它远经理负责处理之类的长路径(超过MAX_PATH)或特殊文件名(con,prn等)就好了.

我创建了一个完全包含255个字符的字符串("12345678901234 ... 012345")并开始创建嵌套目录.幸运的是,Far的"Make Directory"函数采用斜杠分隔的字符串来表示"创建嵌套目录",因此我只需几步就可以通过在内部编辑器中准备一些字符串并进行一些复制和粘贴来完成.

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

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

  1. 我尝试创建一个超过限制一个字符的文件
  2. 远程调用CreateFileW字符串"\\?\ C:\ 123 [...] 012345",其长度为32744个宽字符(不包括终止零).
  3. CreateFileW做一些额外的检查,UNICODE_STRING将以null结尾的字符串转换为(Length = 65488,MaximumLength = 65490)并准备一个OBJECT_ATTRIBUTES结构.
  4. CreateFileW然后调用NtCreateFilentdll.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)所以我想还有一些额外的东西,比如会话或命名空间名称.

编辑3:完成内核后:

  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;
    
    Run Code Online (Sandbox Code Playgroud)

    在我们的例子中,46 + 65476 = 65522(0xfff2)刚刚超过限制.

    所以,神秘解决了(我希望!).

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

  • “C: 是到 \Device\HarddiskVolume1 的符号链接”,哈,所以 Windows 是伪装的 Unix。 (3认同)