Eryk Sun <eryk...@gmail.com> added the comment:
Here's an implementation of _Py_CreateFile2() and win32_xstat_impl(): typedef struct { DWORD type; DWORD attributes; DWORD reparseTag; } _PY_CREATE_FILE_INFO; static HANDLE _Py_CreateFile2(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams, BOOL traverse, _PY_CREATE_FILE_INFO *pCreateInfo) { HANDLE hFile; DWORD error; FILE_BASIC_INFO fbi; FILE_ATTRIBUTE_TAG_INFO fati; BOOL openReparsePoint = FALSE; BOOL traverseFailed = FALSE; _PY_CREATE_FILE_INFO cfi = {0}; CREATEFILE2_EXTENDED_PARAMETERS createExParams = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS)}; if (!pCreateExParams) { pCreateExParams = &createExParams; } pCreateExParams->dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS; if (pCreateExParams->dwFileFlags & FILE_FLAG_OPEN_REPARSE_POINT) { openReparsePoint = TRUE; } else if (!traverse) { pCreateExParams->dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT; } // Share read access if write access isn't requested because // CreateFile2 uses the NT I/O flag FILE_DISALLOW_EXCLUSIVE. if (!(dwDesiredAccess & (GENERIC_ALL | GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA))) { dwShareMode |= FILE_SHARE_READ; } call_createfile: hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, pCreateExParams); error = GetLastError(); // success: 0 or ERROR_ALREADY_EXISTS if (hFile == INVALID_HANDLE_VALUE) { // bpo-37834: open an unhandled reparse point if traverse fails. traverseFailed = (error == ERROR_CANT_ACCESS_FILE); if (openReparsePoint || !(traverse && traverseFailed)) { return INVALID_HANDLE_VALUE; } pCreateExParams->dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT; hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, pCreateExParams); if (hFile == INVALID_HANDLE_VALUE) { SetLastError(error); return INVALID_HANDLE_VALUE; } error = GetLastError(); // 0 or ERROR_ALREADY_EXISTS } if (!pCreateInfo && (openReparsePoint || (traverse && !traverseFailed))) { return hFile; } cfi.type = GetFileType(hFile); if (cfi.type == FILE_TYPE_UNKNOWN && GetLastError() != 0) { error = GetLastError(); goto cleanup; } if (GetFileInformationByHandleEx( hFile, FileAttributeTagInfo, &fati, sizeof(fati))) { cfi.attributes = fati.FileAttributes; cfi.reparseTag = fati.ReparseTag; } else if (GetFileInformationByHandleEx( hFile, FileBasicInfo, &fbi, sizeof(fbi))) { cfi.attributes = fbi.FileAttributes; } else { switch (GetLastError()) { case ERROR_INVALID_PARAMETER: case ERROR_INVALID_FUNCTION: case ERROR_NOT_SUPPORTED: // The file is not in a filesystem. break; default: error = GetLastError(); } goto cleanup; } if (cfi.attributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (IsReparseTagNameSurrogate(cfi.reparseTag)) { if (traverseFailed) { error = ERROR_CANT_ACCESS_FILE; goto cleanup; } } else if (!openReparsePoint && !traverseFailed) { // Always try to reparse if it's not a name surrogate. CloseHandle(hFile); traverse = TRUE; pCreateExParams->dwFileFlags &= ~FILE_FLAG_OPEN_REPARSE_POINT; goto call_createfile; } } cleanup: if (error && error != ERROR_ALREADY_EXISTS) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } else if (pCreateInfo) { *pCreateInfo = cfi; } SetLastError(error); return hFile; } static int win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse) { DWORD error; BY_HANDLE_FILE_INFORMATION fileInfo; _PY_CREATE_FILE_INFO cfi; int retval = 0; HANDLE hFile = _Py_CreateFile2(path, FILE_READ_ATTRIBUTES, 0, OPEN_EXISTING, NULL, traverse, &cfi); if (hFile == INVALID_HANDLE_VALUE) { // Either the path doesn't exist, or the caller lacks access. error = GetLastError(); switch (error) { case ERROR_INVALID_PARAMETER: // The "con" DOS device requires read or write access. hFile = _Py_CreateFile2(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, OPEN_EXISTING, NULL, traverse, &cfi); if (hFile == INVALID_HANDLE_VALUE) { SetLastError(error); return -1; } break; case ERROR_ACCESS_DENIED: // Cannot sync or read attributes. case ERROR_SHARING_VIOLATION: // It's a paging file. // Try reading the parent directory. if (!attributes_from_dir(path, &fileInfo, &cfi.reparseTag)) { // Cannot read the parent directory. SetLastError(error); return -1; } if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (traverse || !IsReparseTagNameSurrogate(cfi.reparseTag)) { // Can't traverse, so fail. SetLastError(error); return -1; } } break; default: return -1; } } if (hFile != INVALID_HANDLE_VALUE) { if (cfi.type != FILE_TYPE_DISK) { memset(result, 0, sizeof(*result)); if (cfi.attributes != INVALID_FILE_ATTRIBUTES && cfi.attributes & FILE_ATTRIBUTE_DIRECTORY) { // e.g. "//./pipe/" or "//./mailslot/" result->st_mode = _S_IFDIR; } else if (cfi.type == FILE_TYPE_CHAR) { // e.g. "//./nul" result->st_mode = _S_IFCHR; } else if (cfi.type == FILE_TYPE_PIPE) { // e.g. "//./pipe/spam" result->st_mode = _S_IFIFO; } // FILE_TYPE_UNKNOWN // e.g. "//./mailslot/waitfor.exe/spam" goto cleanup; } if (!GetFileInformationByHandle(hFile, &fileInfo)) { switch (GetLastError()) { case ERROR_INVALID_PARAMETER: case ERROR_INVALID_FUNCTION: case ERROR_NOT_SUPPORTED: // Volumes and physical disks are block devices, // e.g. "//./C:" or "//./PhysicalDrive0" memset(result, 0, sizeof(*result)); result->st_mode = 0x6000; // S_IFBLK goto cleanup; } retval = -1; goto cleanup; } } _Py_attribute_data_to_stat(&fileInfo, cfi.reparseTag, result); if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { // Set S_IEXEC if the filename has an extension that is // commonly used by files that CreateProcessW can execute. // A real implementation would call GetSecurityInfo, // OpenThreadToken / OpenProcessToken, and AccessCheck to // check for read, write, and execute access. const wchar_t *fileExtension = wcsrchr(path, '.'); if (fileExtension) { if (_wcsicmp(fileExtension, L".com") == 0 || _wcsicmp(fileExtension, L".exe") == 0 || _wcsicmp(fileExtension, L".bat") == 0 || _wcsicmp(fileExtension, L".cmd") == 0) { result->st_mode |= 0111; } } } cleanup: if (hFile != INVALID_HANDLE_VALUE) { // Preserve the last error when failing. error = retval ? GetLastError() : 0; if (CloseHandle(hFile)) { SetLastError(error); } else { retval = -1; } } return retval; } ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue46506> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com