You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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=Noneclosed_at=<Date2019-08-22.04:57:30.847>created_at=<Date2019-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=<Date2019-08-22.06:57:04.538>user='https://github.com/serhiy-storchaka'
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) {
DWORDlastError=GetLastError();
if (lastError==ERROR_INVALID_PARAMETER) {
WCHARfullPath[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;
return0;
}
}
/* Existing error handling code. */
} else {
DWORDtype=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;
} elseif (type==FILE_TYPE_PIPE) { /* e.g. "//./PIPE/Spam" */result->st_mode=_S_IFIFO;
}
return0;
} elseif (!GetFileInformationByHandle(hFile, &info)) {
DWORDlastError=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;
return0;
}
return-1;
}
}
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.
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.
Activity
serhiy-storchaka commentedon May 28, 2019
But os.open() and os.fstat() work.
mangrisano commentedon May 28, 2019
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'.
eryksun commentedon May 28, 2019
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:
eryksun commentedon May 28, 2019
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.
serhiy-storchaka commentedon Jun 10, 2019
Do you mind to create a PR Eryk?
serhiy-storchaka commentedon Aug 22, 2019
Where it was fixed?
eryksun commentedon Aug 22, 2019
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.