Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long paths for python on windows - os.stat() fails for relative paths?

I want to access some long UNC paths on Windows. I know that I need to use the "\\?\UNC\" prefix (which is "\\\\?\\UNC\\" if you escape the slashes). That works fine:

os.stat('\\\\?\\UNC\\server.example.com\\that\\has\\long\\path\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.txt')
# works, returns os.stat_result

However, it seems to fail with a relative path:

os.chdir('\\\\?\\UNC\\server.example.com\\that\\has\\long\\path')
os.getcwd()
# returns '\\\\?\\UNC\\server.example.com\\that\\has\\long\\path'
os.stat('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.txt')
# fails with [WinError 3] The system cannot find the path specified: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.txt'

Might this be a bug in Python, or is my code wrong?

Sidenote - a workaround is os.stat(os.path.abspath('aaa\\bbb.txt')).

like image 425
pianoJames Avatar asked Mar 01 '18 16:03

pianoJames


1 Answers

In Windows 10, you can enable long-path support for the system by setting a DWORD named "LongPathsEnabled" in "HKLM\System\CurrentControlSet\Control\FileSystem". This allows applications that declare support for long paths in their manifest to use the maximum path length that's supported by the kernel (about 32,760 characters, depending on the final resolved path), without even requiring the "\\?\" prefix. Python 3.6+ is manifested to support long paths.

That said, prior to Windows 10, the working directory and relative paths cannot exceed MAX_PATH (260) characters, which includes the trailing backslash and NUL terminator. The current documentation is misleading on this point. Apparently someone added the disclaimer "to extend this limit to 32,767 wide characters..." to the docs for SetCurrentDirectory. No, there's no extending the limit. Here's what it said circa 2016.

The current working directory of a process is a DOS path, as opposed to a native kernel path (*). A DOS path is any path that's non-Unicode, or uses forward slashes, DOS devices (e.g. logical drive letters, CON, NUL, etc), or UNC syntax. DOS paths have to be translated to native paths by runtime-library functions in ntdll.dll. If long-path support isn't available, this implicit translation is limited to at most MAX_PATH characters.

Working around this requires using a fully-qualified Unicode path that begins with the "\\?\" prefix. This prefix tells the runtime-library to bypass path translation. Instead it simply replaces the "\\?\" prefix with the kernel's "\??\" virtual directory for DOS device links, and the path ultimately resolves to a real NT device (e.g. "\\?\UNC" => "\??\UNC" => "\Device\Mup").


(*) The kernel namespace uses a singly-rooted tree for all kernel objects, not just device objects. It also has a more reliable way of handling relative paths; see the RootDirectory field of OBJECT_ATTRIBUTES.

like image 137
Eryk Sun Avatar answered Nov 15 '22 03:11

Eryk Sun