Skip to content

os.stat() does not work for NUL and CON #81255

Closed

Description

@serhiy-storchaka
BPO 37074
Nosy @pfmoore, @tjguk, @zware, @serhiy-storchaka, @eryksun, @zooba

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2019-08-22.04:57:30.847>
created_at = <Date 2019-05-28.07:37:41.082>
labels = ['3.8', 'type-bug', '3.7', 'OS-windows']
title = 'os.stat() does not work for NUL and CON'
updated_at = <Date 2019-08-22.06:57:04.538>
user = 'https://github.com/serhiy-storchaka'

bugs.python.org fields:

activity = <Date 2019-08-22.06:57:04.538>
actor = 'eryksun'
assignee = 'none'
closed = True
closed_date = <Date 2019-08-22.04:57:30.847>
closer = 'eryksun'
components = ['Windows']
creation = <Date 2019-05-28.07:37:41.082>
creator = 'serhiy.storchaka'
dependencies = []
files = []
hgrepos = []
issue_num = 37074
keywords = []
message_count = 7.0
messages = ['343745', '343755', '343781', '343783', '345107', '350158', '350166']
nosy_count = 6.0
nosy_names = ['paul.moore', 'tim.golden', 'zach.ware', 'serhiy.storchaka', 'eryksun', 'steve.dower']
pr_nums = []
priority = 'normal'
resolution = 'out of date'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue37074'
versions = ['Python 2.7', 'Python 3.7', 'Python 3.8']

Activity

  1. serhiy-storchaka commented on May 28, 2019

    @serhiy-storchaka
    MemberAuthor
    >>> os.stat('nul')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [WinError 1] Incorrect function: 'nul'
    >>> os.stat('con')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [WinError 87] The parameter is incorrect: 'con'

    But os.open() and os.fstat() work.

    >>> os.fstat(os.open('nul', os.O_RDONLY))
    os.stat_result(st_mode=8192, st_ino=0, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=0, st_atime=0, st_mtime=0, st_ctime=0)
    >>> os.fstat(os.open('con', os.O_RDONLY))
    os.stat_result(st_mode=8192, st_ino=0, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=0, st_atime=0, st_mtime=0, st_ctime=0)
  2. mangrisano commented on May 28, 2019

    mangrisanomannequin
    Mannequin

    I've tried to reproduce this behavior on my Mac with python3.8 and python 3.7 but I couldn't.

    If 'nul' doesn't exist, it raises a FileNotFound exception as it should do.
    If 'nul' exists, it shows me the right output.
    Same behavior with 'con'.

  3. eryksun commented on May 28, 2019

    @eryksun
    Contributor

    Opening "CON" (i.e. r"\\.\CON") fails with ERROR_INVALID_PARAMETER (87) because it has to be opened with either GENERIC_READ or GENERIC_WRITE data access in order to map it to either the console input buffer or active screen buffer. The CreateFileW call in stat() necessarily requests no data access.

    Calling stat on "NUL" (among others such as "CONIN$", "//./C:", and "//./PhysicalDrive0") fails with ERROR_INVALID_FUNCTION (1) -- or possibly ERROR_INVALID_PARAMETER or ERROR_NOT_SUPPORTED (50), depending on the device. stat() calls GetFileInformationByHandle. This requests FileFsVolumeInformation and FileAllInformation, which commonly fail as unsupported or invalid requests for devices other than filesystem devices. Even volume and raw disk devices fail a FileAllInformation request.

    If we have a valid file handle, we can get the file type via GetFileType(hFile), as _Py_fstat_noraise does in Python/fileutils.c. If opening a handle fails with ERROR_INVALID_PARAMETER for a path that resolves to r"\\.\CON" or r"\\?\CON" via GetFullPathNameW, we can simply set st_mode to _S_IFCHR and return.

    For example:

        if (hFile == INVALID_HANDLE_VALUE) {
            DWORD lastError = GetLastError();
            if (lastError == ERROR_INVALID_PARAMETER) {
                WCHAR fullPath[8];
                if (GetFullPathNameW(path, sizeof(fullPath),
                        fullPath, NULL) == 7 && (
                    _wcsicmp(fullPath, L"\\\\.\\CON") == 0 ||
                    _wcsicmp(fullPath, L"\\\\?\\CON") == 0)) {
                    memset(result, 0, sizeof(*result));
                    result->st_mode = _S_IFCHR;
                    return 0;
                }
            }
            /*
                Existing error handling code.
            */
        } else {
            DWORD type = GetFileType(hFile);
            if (type != FILE_TYPE_DISK) {
                CloseHandle(hFile);
                if (type == FILE_TYPE_UNKNOWN && GetLastError() != 0) {
                    return -1;
                }
                memset(result, 0, sizeof(*result));
                if (type == FILE_TYPE_CHAR) { /* e.g. "//./NUL" */
                    result->st_mode = _S_IFCHR;
                } else if (type == FILE_TYPE_PIPE) { /* e.g. "//./PIPE/Spam" */
                    result->st_mode = _S_IFIFO;
                }
                return 0;
            } else if (!GetFileInformationByHandle(hFile, &info)) {
                DWORD lastError = GetLastError();
                CloseHandle(hFile);
                /* e.g. "//./C:" or "//./PhysicalDrive0" */
                if (lastError == ERROR_INVALID_FUNCTION ||
                    lastError == ERROR_INVALID_PARAMETER ||
                    lastError == ERROR_NOT_SUPPORTED) {
                    memset(result, 0, sizeof(*result));
                    result->st_mode = _S_IFREG;
                    return 0;
                }
                return -1;
            }
        }
  4. eryksun commented on May 28, 2019

    @eryksun
    Contributor

    GetFullPathNameW(path, sizeof(fullPath),

    That should be sizeof(fullPath) / sizeof(WCHAR), or use Py_ARRAY_LENGTH, or just hard code 8. That's probably not the only mistake. But this is just an example to discuss the details and alternatives.

  5. serhiy-storchaka commented on Jun 10, 2019

    @serhiy-storchaka
    MemberAuthor

    Do you mind to create a PR Eryk?

  6. serhiy-storchaka commented on Aug 22, 2019

    @serhiy-storchaka
    MemberAuthor

    Where it was fixed?

  7. eryksun commented on Aug 22, 2019

    @eryksun
    Contributor

    Where it was fixed?

    It was addressed in bpo-37834. PR 15231 includes a rewrite of win32_xstat_impl. The file type was needed, so the rewrite could also address the problem of character devices such as CON and NUL.

  8. transferred this issue fromon Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions