New submission from Eryk Sun: pathlib._WindowsFlavour.is_reserved assumes Windows uses an exact match up to the file extension for reserved DOS device names. However, this misses cases involving trailing spaces and colons, such as the following examples:
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 Windows calls RtlIsDosDeviceName_Ustr to check whether a path represents a DOS device name. Here's a link to the reverse-engineered implementation of this function in ReactOS 4.1: http://code.reactos.org/browse/reactos/branches/ros-branch-0_4_1/reactos/sdk/lib/rtl/path.c?r=71210#to85 The ReactOS implementation performs the following steps: * Return false for a UNC or unknown path type or an empty path. * Strip a final ":" if present. Return false if it was the only character. * Strip trailing dot and space characters. * Iterate over the path in reverse. If the current character is a "\\" or "/" or a ":" drive letter separator (at index 1), then if the next character matches the first letter of a DOS device name, splice out the base name as a potential match. Else return false. * Return false if the first character at this point does not match the first letter of a DOS device name. * Remove the file extension, starting at the first dot or colon. * Remove trailing spaces. * Return the name offset and length if it equals * "COM" or "LPT" plus a digit * "PRN", "AUX", "NUL, or "CON" * Else return false. It seems that ":" and "." are effectively equivalent for the purposes of is_reserved. Given this is the case, it could return whether parts[-1].partition('.')[0].partition(':')[0].rstrip(' ').upper() is in self.reserved_names. Or maybe use a regex for the entire check. If a script is running on Windows, I think the best approach is to call os.path.abspath, which calls _getfullpathname. This lets Windows itself determine if the path maps to the \\.\ device namespace. However, I realize that is_reserved is intended to be cross-platform. By the way, the comment for this method says that r"foo\NUL" isn't reserved, but it is. Maybe the author checked by trying to open NUL in a non-existing foo directory. DOS device names are only reserved in practice when opening and creating files in existing directories (as opposed to reserved in principle with GetFullPathName, which doesn't check for a valid path). NT can thus return an error that's consistent with how DOS behaved in the 1980s -- because that's really important, you know. ---------- components: Library (Lib), Windows messages: 273344 nosy: eryksun, paul.moore, steve.dower, tim.golden, zach.ware priority: normal severity: normal stage: needs patch status: open title: pathlib is_reserved fails for some reserved paths on Windows type: behavior versions: Python 3.5, Python 3.6 _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue27827> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com