路径名太长打不开? [英] Pathname too long to open?

查看:124
本文介绍了路径名太长打不开?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是执行的截图:

如您所见,错误表明目录JSONFiles/Apartment/Rent/dubizzleabudhabiproperty"不存在.

As you see, the error says that the directory "JSONFiles/Apartment/Rent/dubizzleabudhabiproperty" is not there.

但是请看我的文件:

文件夹肯定在那里.

代码

self.file = open("JSONFiles/"+ item["category"]+"/" + item["action"]+"/"+ item['source']+"/"+fileName + '.json', 'wb') # Create a new JSON file with the name = fileName parameter
        line = json.dumps(dict(item)) # Change the item to a JSON format in one line
        self.file.write(line) # Write the item to the file

更新

当我将文件名更改为较小的文件名时,它可以工作,因此问题在于路径的长度.请问有什么解决办法?

UPDATE

When I change the file name to a smaller one, it works, so the problem is because of the length of the path. what is the solution please?

推荐答案

常规 DOS 路径仅限于 MAX_PATH (260) 个字符,包括字符串的终止 NUL 字符.您可以使用以 \\?\ 前缀开头的扩展长度路径来超过此限制.此路径必须是 Unicode 字符串、完全限定的,并且只能使用反斜杠作为路径分隔符.根据 Microsoft 的文件系统功能比较,最大扩展路径长度为 32760 个字符.单个文件或目录名称最多可包含 255 个字符(UDF 文件系统为 127 个字符).也支持扩展 UNC 路径为 \\?\UNC\server\share.

Regular DOS paths are limited to MAX_PATH (260) characters, including the string's terminating NUL character. You can exceed this limit by using an extended-length path that starts with the \\?\ prefix. This path must be a Unicode string, fully qualified, and only use backslash as the path separator. Per Microsoft's file system functionality comparison, the maximum extended path length is 32760 characters. A individual file or directory name can be up to 255 characters (127 for the UDF filesystem). Extended UNC paths are also supported as \\?\UNC\server\share.

例如:

import os

def winapi_path(dos_path, encoding=None):
    if (not isinstance(dos_path, unicode) and 
        encoding is not None):
        dos_path = dos_path.decode(encoding)
    path = os.path.abspath(dos_path)
    if path.startswith(u"\\\\"):
        return u"\\\\?\\UNC\\" + path[2:]
    return u"\\\\?\\" + path

path = winapi_path(os.path.join(u"JSONFiles", 
                                item["category"],
                                item["action"], 
                                item["source"], 
                                fileName + ".json"))

>>> path = winapi_path("C:\\Temp\\test.txt")
>>> print path
\\?\C:\Temp\test.txt

请参阅 MSDN 上的以下页面:

See the following pages on MSDN:

Windows 调用 NT 运行时库函数 RtlDosPathNameToRelativeNtPathName_U_WithStatus 将 DOS 路径转换为原生 NT 路径.如果我们打开(即CreateFile)上面的路径并在后一个函数上设置断点,我们可以看到它如何处理以\\开头的路径?\ 前缀.

Windows calls the NT runtime library function RtlDosPathNameToRelativeNtPathName_U_WithStatus to convert a DOS path to a native NT path. If we open (i.e. CreateFile) the above path with a breakpoint set on the latter function, we can see how it handles a path that starts with the \\?\ prefix.

Breakpoint 0 hit
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus:
00007ff9`d1fb5880 4883ec58        sub     rsp,58h
0:000> du @rcx
000000b4`52fc0f60  "\\?\C:\Temp\test.txt"
0:000> r rdx
rdx=000000b450f9ec18
0:000> pt
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus+0x66:
00007ff9`d1fb58e6 c3              ret

结果将 \\?\ 替换为 NT DOS 设备前缀 \??\,并将字符串复制到本机 UNICODE_STRING:

The result replaces \\?\ with the NT DOS devices prefix \??\, and copies the string into a native UNICODE_STRING:

0:000> dS b450f9ec18
000000b4`536b7de0  "\??\C:\Temp\test.txt"

如果您使用 //?/ 而不是 \\?\,则路径仍然限制为 MAX_PATH 个字符.如果它太长,则 RtlDosPathNameToRelativeNtPathName 返回状态代码 STATUS_NAME_TOO_LONG (0xC0000106).

If you use //?/ instead of \\?\, then the path is still limited to MAX_PATH characters. If it's too long, then RtlDosPathNameToRelativeNtPathName returns the status code STATUS_NAME_TOO_LONG (0xC0000106).

如果您使用 \\?\ 作为前缀,但在路径的其余部分使用斜线,Windows 不会为您将斜线转换为反斜线:

If you use \\?\ for the prefix but use slash in the rest of the path, Windows will not translate the slash to backslash for you:

Breakpoint 0 hit
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus:
00007ff9`d1fb5880 4883ec58        sub     rsp,58h
0:000> du @rcx
0000005b`c2ffbf30  "\\?\C:/Temp/test.txt"
0:000> r rdx
rdx=0000005bc0b3f068
0:000> pt
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus+0x66:
00007ff9`d1fb58e6 c3              ret
0:000> dS 5bc0b3f068
0000005b`c3066d30  "\??\C:/Temp/test.txt"

正斜杠是 NT 命名空间中的有效对象名称字符.它由 Microsoft 文件系统保留,但您可以在其他命名内核对象中使用正斜杠,这些对象存储在 \BaseNamedObjects\Sessions\[session number]\BaseNamedObjects 中.此外,我认为 I/O 管理器不会对设备和文件名中的保留字符执行策略.这取决于设备.也许有人在那里有一个 Windows 设备,它实现了一个允许名称中的正斜杠的命名空间.至少您可以创建包含正斜杠的 DOS 设备名称.例如:

Forward slash is a valid object name character in the NT namespace. It's reserved by Microsoft filesystems, but you can use a forward slash in other named kernel objects, which get stored in \BaseNamedObjects or \Sessions\[session number]\BaseNamedObjects. Also, I don't think the I/O manager enforces the policy on reserved characters in device and filenames. It's up to the device. Maybe someone out there has a Windows device that implements a namespace that allows forward slash in names. At the very least you can create DOS device names that contain a forward slash. For example:

>>> kernel32 = ctypes.WinDLL('kernel32')
>>> kernel32.DefineDosDeviceW(0, u'My/Device', u'C:\\Temp')
>>> os.path.exists(u'\\\\?\\My/Device\\test.txt')
True

您可能想知道 \?? 是什么意思.这曾经是对象命名空间中 DOS 设备链接的实际目录,但从 NT 5(或 NT 4 w/终端服务)开始,它变成了一个虚拟前缀.对象管理器通过首先检查 \Sessions\0\DosDevices\[LOGON_SESSION_ID] 目录中的登录会话的 DOS 设备链接,然后检查 目录中的系统范围的 DOS 设备链接来处理这个前缀.\Global?? 目录.

You may be wondering what \?? signifies. This used to be an actual directory for DOS device links in the object namespace, but starting with NT 5 (or NT 4 w/ Terminal Services) this became a virtual prefix. The object manager handles this prefix by first checking the logon session's DOS device links in the directory \Sessions\0\DosDevices\[LOGON_SESSION_ID] and then checking the system-wide DOS device links in the \Global?? directory.

请注意,前者是登录会话,而不是 Windows 会话.登录会话目录都在 Windows 会话 0(即 Vista+ 中的服务会话)的 DosDevices 目录下.因此,如果您有用于非提升登录的映射驱动器,您会发现它在提升的命令提示符中不可用,因为您提升的令牌实际上用于不同的登录会话.

Note that the former is a logon session, not a Windows session. The logon session directories are all under the DosDevices directory of Windows session 0 (i.e. the services session in Vista+). Thus if you have a mapped drive for a non-elevated logon, you'll discover that it's not available in an elevated command prompt, because your elevated token is actually for a different logon session.

DOS 设备链接的一个例子是 \Global??\C: => \Device\HarddiskVolume2.在这种情况下,DOS C: 驱动器实际上是到 HarddiskVolume2 设备的符号链接.

An example of a DOS device link is \Global??\C: => \Device\HarddiskVolume2. In this case the DOS C: drive is actually a symbolic link to the HarddiskVolume2 device.

这里简要概述了系统如何处理解析路径以打开文件.鉴于我们正在调用 WinAPI CreateFile,它将翻译后的 NT UNICODE_STRING 存储在 OBJECT_ATTRIBUTES 结构并调用系统函数NtCreateFile.

Here's a brief overview of how the system handles parsing a path to open a file. Given we're calling WinAPI CreateFile, it stores the translated NT UNICODE_STRING in an OBJECT_ATTRIBUTES structure and calls the system function NtCreateFile.

0:000> g
Breakpoint 1 hit
ntdll!NtCreateFile:
00007ff9`d2023d70 4c8bd1          mov     r10,rcx
0:000> !obja @r8
Obja +000000b450f9ec58 at 000000b450f9ec58:
        Name is \??\C:\Temp\test.txt
        OBJ_CASE_INSENSITIVE

NtCreateFile 调用 I/O 管理器函数 IoCreateFile,它依次调用未记录的对象管理器 API ObOpenObjectByName.这完成了解析路径的工作.对象管理器以 \??\C:\Temp\test.txt 开头.然后它用 \Global??\C:Temp\test.txt 替换它.接下来,它解析到 C: 符号链接,并且必须重新开始(重新解析)最终路径 \Device\HarddiskVolume2\Temp\test.txt.

NtCreateFile calls the I/O manager function IoCreateFile, which in turn calls the undocumented object manager API ObOpenObjectByName. This does the work of parsing the path. The object manager starts with \??\C:\Temp\test.txt. Then it replaces that with \Global??\C:Temp\test.txt. Next it parses up to the C: symbolic link and has to start over (reparse) the final path \Device\HarddiskVolume2\Temp\test.txt.

一旦对象管理器获取到 HarddiskVolume2 设备对象,解析就交给 I/O 管理器,它实现了 Device 对象类型.I/O DeviceParseProcedure 创建 File 对象和 I/O 请求数据包 (IRP)主要函数代码 IRP_MJ_CREATE(打开/创建操作)由设备堆栈处理.这通过 IoCallDriver 发送到设备驱动程序.如果设备实现了重解析点(例如连接挂载点、符号链接等)并且路径包含重解析点,则必须将解析的路径重新提交给对象管理器以从头开始解析.

Once the object manager gets to the HarddiskVolume2 device object, parsing is handed off to the I/O manager, which implements the Device object type. The ParseProcedure of an I/O Device creates the File object and an I/O Request Packet (IRP) with the major function code IRP_MJ_CREATE (an open/create operation) to be processed by the device stack. This is sent to the device driver via IoCallDriver. If the device implements reparse points (e.g. junction mountpoints, symbolic links, etc) and the path contains a reparse point, then the resolved path has to be resubmitted to the object manager to be parsed from the start.

设备驱动程序将使用进程令牌(或模拟线程)的 SeChangeNotifyPrivilege(几乎总是存在并启用)在遍历目录时绕过访问检查.但是,最终访问设备和目标文件必须由安全描述符允许,通过 SeAccessCheck.除了简单的文件系统如 FAT32 不支持文件安全.

The device driver will use the SeChangeNotifyPrivilege (almost always present and enabled) of the process token (or thread if impersonating) to bypass access checks while traversing directories. However, ultimately access to the device and target file has to be allowed by a security descriptor, which is verified via SeAccessCheck. Except simple filesystems such as FAT32 don't support file security.

这篇关于路径名太长打不开?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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