当文件路径超过大约时,内部会发生什么?Windows 中有 32767 个字符? [英] What happens internally when a file path exceeds approx. 32767 characters in Windows?

查看:41
本文介绍了当文件路径超过大约时,内部会发生什么?Windows 中有 32767 个字符?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In Windows (assume 2000 onwards), a file path can be at most approximately 32767 characters in length. This limitation exists due to the internal handling with UNICODE_STRING in the native API (also on the kernel side, in drivers etc). So far so good. I know the theory behind that part.

限制的原因是UNICODE_STRINGLengthMaximumLength成员统计了Buffer中的字节数,但本身是 16 位无符号整数.

The reason for the limit is that the the Length and MaximumLength members of UNICODE_STRING count the number of bytes in the Buffer, but are 16 bit unsigned integers themselves.

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

I also know why the limit is an approximation rather than a set limit. This is mostly due to how your file name (e.g. \\.\C:\boot.ini) gets resolved to its native form (e.g. \??\C:\boot.ini) and then to something that is prefixed by the actual volume device name and then followed by the path relative to that volume, e.g. \Device\HarddiskVolume2\boot.ini.

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

Furthermore from Windows Explorer the known symptom when hitting the ("ANSI") MAX_PATH limit is to pretend the file or folder doesn't exist in some versions of Windows (possible this got fixed at some point).

但是当我使用类似于 的路径调用 CreateFile() 时,对象管理器、I/O 管理器和文件系统驱动程序级别分别会发生什么\\.\C:\...\filename.ext 并且整个路径没有超过限制,但达到它,在我的调用中kernel32.dllCreateFile()then 被扩展了吗?...

But what happens at the object manager, I/O manager and file system driver levels respectively when I call CreateFile() with a path that looks like \\.\C:\...\filename.ext and the whole path does not exceed the limit, but reaches it, in my call to kernel32.dll's CreateFile() and then gets expanded? ...

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

Neither the SDKs nor the WDKs seem to be particularly chatty about the topic. Or did I look in the wrong sections?

推荐答案

因为我很懒,所以没有写测试程序,而是使用优秀的 Far Manager 处理长路径(长于 MAX_PATH)或特殊文件名(conprn 等)) 就好了.

Because I'm lazy, I didn't write a test program but tested it using the excellent Far Manager which handles things like long paths (longer than MAX_PATH) or special filenames (con, prn etc) just fine.

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

I made a string of exactly 255 characters ("12345678901234...012345") and started creating nested directories. Luckily, Far's "Make Directory" function takes a slash-separated string to mean "create nested directories" so I was able to do it in just a few steps by preparing a string in the internal editor with some copy&paste.

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

The longest path I was able to create was 32739 characters long, counting from "C:\" (i.e. it does not include "\\?\" added by Far). The error that I get when trying to create a directory or file with just one additional character is "The filename or extension is too long.". If I try to enter that directory, I get the same error.

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

EDIT: spent some time in the debugger and here's what happens on the Win32 API level:

  1. 我尝试创建一个超过限制一个字符的文件
  2. Far 使用字符串\\?\C:\123[...]012345"调用 CreateFileW32744 个宽字符长(不包括终止零).
  3. CreateFileW 做一些额外的检查,将空终止字符串转换为 UNICODE_STRING(长度=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).
  1. I try to create a file with one character above the limit
  2. Far calls CreateFileW with the string "\\?\C:\123[...]012345" which is 32744 wide characters long (not counting the terminating zero).
  3. CreateFileW does some extra checks, converts the null-terminated string to UNICODE_STRING (Length=65488, MaximumLength=65490) and prepares an OBJECT_ATTRIBUTES struct.
  4. CreateFileW then calls NtCreateFile in ntdll.dll, which is just a wrapper around syscall instruction.
  5. NtCreateFile returns 0xC0000106 (STATUS_NAME_TOO_LONG).
  6. That status value is then converted (using RtlNtStatusToDosError) to the Win32 error 206 (ERROR_FILENAME_EXCED_RANGE).

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

EDIT2:我运行了 WinObj 并在我的system C:\Device\HarddiskVolume1 的符号链接.此字符串的长度为23 个字符.如果我们将传递给 NtCreateFile 的字符串中的 \C: 替换为它,我们将得到 32744 - 3 + 23 = 32764 个字符.连同终止零,这需要 65530 个字节.仍然低于限制 (0xFFFF=65535),所以我想添加了一些额外的东西,比如会话或命名空间名称.

EDIT2: I ran WinObj and found that on my system C: is a symlink to \Device\HarddiskVolume1. This string is 23 characters long. If we replace the \C: in the string passed to NtCreateFile with it, we get 32744 - 3 + 23 = 32764 characters. Together with the terminating zero, this requires 65530 bytes. Still short of the limit (0xFFFF=65535) so I guess there's something extra being added, like a session or namespace name.

EDIT3:经过内核后:

  1. NtCreateFile 调用 IopCreateFile
  2. IopCreateFile 调用 ObOpenObjectByName
  3. ObOpenObjectByName 调用 ObpLookupObjectName
  4. ObpLookupObjectName 检查 ObpDosDevicesShortNamePrefix ("\??\") -> 成功
  5. 它跳过前缀并将剩余部分拆分为 "C:""\1234..."
  6. 它通过调用 ObpLookupDirectoryEntry
  7. 解析 "C:"
  8. 然后调用 ObpParseSymbolicLink 将查找的目录条目(_OBJECT_SYMBOLIC_LINKLinkTarget =="\Device\HarddiskVolume1"DosDeviceDriveIndex == 3) 和名称的其余部分.
  9. 然后它会做这样的事情(忠实地ReactOS 转载):

  1. NtCreateFile calls IopCreateFile
  2. IopCreateFile calls ObOpenObjectByName
  3. ObOpenObjectByName calls ObpLookupObjectName
  4. ObpLookupObjectName checks for ObpDosDevicesShortNamePrefix ("\??\") -> success
  5. it skips the prefix and splits the remaining part into "C:" and "\1234..."
  6. it resolves the "C:" with a call to ObpLookupDirectoryEntry
  7. it then calls ObpParseSymbolicLink passing to it the looked-up directory entry (_OBJECT_SYMBOLIC_LINK with LinkTarget == "\Device\HarddiskVolume1" and DosDeviceDriveIndex == 3) and the remaining part of the name.
  8. It then does something like this (faithfully reproduced by ReactOS):

TargetPath = &SymlinkObject->LinkTarget;
TempLength = TargetPath->Length;
TotalLength = TempLength + RemainingName->Length;
if (LengthUsed > 0xFFF0)
    return STATUS_NAME_TOO_LONG;

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

In our case, 46 + 65476 = 65522 (0xfff2) which is just above the limit.

到这里,谜团解开了(我希望!).

So there, mystery solved (I hope!).

附言一切都在 Windows 7 x64 SP1 下测试过.

P.S. everything tested under Windows 7 x64 SP1.

这篇关于当文件路径超过大约时,内部会发生什么?Windows 中有 32767 个字符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆