Does This Scare You?

eryk sun eryksun at gmail.com
Sun Aug 21 20:38:15 EDT 2016


On Sun, Aug 21, 2016 at 8:03 PM, Michael Torrie <torriem at gmail.com> wrote:
> On 08/19/2016 05:42 PM, Lawrence D’Oliveiro wrote:
>>     Python 3.5.2+ (default, Aug  5 2016, 08:07:14)
>>     [GCC 6.1.1 20160724] on linux
>>     Type "help", "copyright", "credits" or "license" for more information.
>>     >>> from pathlib import PureWindowsPath
>>     >>> PureWindowsPath("prn").is_reserved()
>>     True
>>     >>> PureWindowsPath("prn.doc").is_reserved()
>>     True
>>     >>> PureWindowsPath("com9.exe").is_reserved()
>>     True
>>     >>> PureWindowsPath("c:/my documents/prn.doc").is_reserved()
>>     True
>
> Which part are you getting at?  That Windows treats certain filenames as
> reserved (a known gotcha that has existed for decades) or that Python
> allows you to test whether a path is valid in Windows?

To me it's scary that this check misses cases because it's trying to
be cross-platform instead of simply relying on GetFullPathName to do
the work. For example, it misses at least the following cases:

Optional trailing colon:

    >>> pathlib.Path('C:/foo/NUL:').is_reserved()
    False
    >>> print(os.path._getfullpathname('C:/foo/NUL:'))
    \\.\NUL

Trailing spaces:

    >>> pathlib.Path('C:/foo/NUL  ').is_reserved()
    False
    >>> print(os.path._getfullpathname('C:/foo/NUL  '))
    \\.\NUL

Trailing spaces followed by a file extension:

    >>> pathlib.Path('C:/foo/NUL  .txt').is_reserved()
    False
    >>> print(os.path._getfullpathname('C:/foo/NUL  .txt'))
    \\.\NUL

It's also a bit disappointing that the author of this function claims
in a comment that "foo/NUL" isn't reserved yet decides to "err on the
side of caution" (obviously not enough). Of course "foo/NUL" is
reserved:

    >>> print(os.path._getfullpathname('foo/NUL'))
    \\.\NUL

I think what happened is that the author tested by calling open() on a
non-existing path. DOS device names are only reserved for existing
directories, in order to return an error for an invalid path. The
difference is how RtlGetFullPathName_Ustr is called. When
GetFullPathName calls the latter function it doesn't care whether or
not the path is valid. On the other hand, RtlDosPathNameToNtPathName_*
calls RtlGetFullPathName_Ustr with a parameter to check for an invalid
path, which makes the path normalization fail. For example:

Existing directory:

    >>> os.path.exists('C:/Temp')
    True
    >>> f = open('C:/Temp/NUL')

    Query the device name:

    >>> hFile = msvcrt.get_osfhandle(f.fileno())
    >>> ntdll.NtQueryObject(hFile, 1, byref(name), sizeof(name), None)
    0
    >>> print(name.Buffer[:name.Length//2])
    \Device\Null

    (\\.\NUL, i.e. \GLOBAL??\NUL, is a symbolic link to NT's \Device\Null.)

Non-existing directory:

    >>> os.path.exists('C:/Spam')
    False
    >>> g = open('C:/Spam/NUL')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    FileNotFoundError: [Errno 2] No such file or directory: 'C:/Spam/NUL'



More information about the Python-list mailing list