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.dll
的CreateFile()
,并随后得到扩大呢?...
SDK和WDK似乎都没有特别讨论这个话题.还是我看错了?
Igo*_*sky 38
因为我很懒,我没有写一个测试程序,但使用优秀测试它远经理负责处理之类的长路径(超过MAX_PATH
)或特殊文件名(con
,prn
等)就好了.
我创建了一个完全包含255个字符的字符串("12345678901234 ... 012345")并开始创建嵌套目录.幸运的是,Far的"Make Directory"函数采用斜杠分隔的字符串来表示"创建嵌套目录",因此我只需几步就可以通过在内部编辑器中准备一些字符串并进行一些复制和粘贴来完成.
我能够创建的最长路径长度为32739个字符,从"C:\"开始计算(即它不包括由Far添加的"\\?\").尝试使用一个附加字符创建目录或文件时出现的错误是" 文件名或扩展名太长了. ".如果我尝试进入该目录,我会得到同样的错误.
编辑:花了一些时间在调试器中,这是在Win32 API级别上发生的事情:
CreateFileW
字符串"\\?\ C:\ 123 [...] 012345",其长度为32744个宽字符(不包括终止零).CreateFileW
做一些额外的检查,UNICODE_STRING
将以null结尾的字符串转换为(Length = 65488,MaximumLength = 65490)并准备一个OBJECT_ATTRIBUTES
结构.CreateFileW
然后调用NtCreateFile
中ntdll.dll
,这仅仅是围绕一个包装syscall
指令.NtCreateFile
返回0xC0000106(STATUS_NAME_TOO_LONG
).RtlNtStatusToDosError
将该状态值转换(使用)到Win32错误206(ERROR_FILENAME_EXCED_RANGE
).我没有费心去检查内核中发生了什么,但我想我也可以看一下.
EDIT2:我运行WinObj,发现在我的系统上C:
是一个符号链接\Device\HarddiskVolume1
.该字符串长度为23个字符.如果我们用它替换\C:
传递给NtCreateFile
它的字符串,我们得到32744 - 3 + 23 = 32764个字符.与终止零一起,这需要65530个字节.仍然没有超出限制(0xFFFF = 65535)所以我想还有一些额外的东西,比如会话或命名空间名称.
编辑3:完成内核后:
NtCreateFile
电话 IopCreateFile
IopCreateFile
电话 ObOpenObjectByName
ObOpenObjectByName
电话 ObpLookupObjectName
ObpLookupObjectName
检查ObpDosDevicesShortNamePrefix
("\??\"
) - >成功"C:"
和"\1234..."
"C:"
通过调用解决了ObpLookupDirectoryEntry
ObpParseSymbolicLink
查找的目录条目(_OBJECT_SYMBOLIC_LINK
带有LinkTarget
== "\Device\HarddiskVolume1"
和DosDeviceDriveIndex
== 3)和名称的剩余部分.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下测试的所有内容.