Skip to content

Commit 0d71cc4

Browse files
authored
gh-152433: Windows: modernize and refactor stat emulation APIs (GH-153700)
This allows build on more API sets (e.g. UWP) without excluding any currently supported desktop OS.
1 parent eef2349 commit 0d71cc4

3 files changed

Lines changed: 54 additions & 59 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Modernize fileutils removing GetFileInformationByHandle API calls and allow
2+
build for Universal Windows Platform.

Modules/posixmodule.c

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -829,9 +829,9 @@ PyOS_AfterFork(void)
829829
#ifdef MS_WINDOWS
830830
/* defined in fileutils.c */
831831
void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
832-
void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG,
833-
FILE_BASIC_INFO *, FILE_ID_INFO *,
834-
struct _Py_stat_struct *);
832+
void _Py_attribute_data_to_stat(FILE_STANDARD_INFO*, ULONG,
833+
FILE_BASIC_INFO*, FILE_ID_INFO*,
834+
struct _Py_stat_struct*);
835835
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *,
836836
struct _Py_stat_struct *);
837837
#endif
@@ -2025,25 +2025,31 @@ win32_wchdir(LPCWSTR path)
20252025

20262026
static void
20272027
find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
2028-
BY_HANDLE_FILE_INFORMATION *info,
2028+
FILE_BASIC_INFO* basic_info,
2029+
FILE_STANDARD_INFO* standard_info,
20292030
ULONG *reparse_tag)
20302031
{
2031-
memset(info, 0, sizeof(*info));
2032-
info->dwFileAttributes = pFileData->dwFileAttributes;
2033-
info->ftCreationTime = pFileData->ftCreationTime;
2034-
info->ftLastAccessTime = pFileData->ftLastAccessTime;
2035-
info->ftLastWriteTime = pFileData->ftLastWriteTime;
2036-
info->nFileSizeHigh = pFileData->nFileSizeHigh;
2037-
info->nFileSizeLow = pFileData->nFileSizeLow;
2038-
/* info->nNumberOfLinks = 1; */
2032+
memset(basic_info, 0, sizeof(*basic_info));
2033+
memset(standard_info, 0, sizeof(*standard_info));
2034+
2035+
basic_info->FileAttributes = pFileData->dwFileAttributes;
2036+
basic_info->CreationTime.HighPart = pFileData->ftCreationTime.dwHighDateTime;
2037+
basic_info->CreationTime.LowPart = pFileData->ftCreationTime.dwLowDateTime;
2038+
basic_info->LastAccessTime.HighPart = pFileData->ftLastAccessTime.dwHighDateTime;
2039+
basic_info->LastAccessTime.LowPart = pFileData->ftLastAccessTime.dwLowDateTime;
2040+
basic_info->LastWriteTime.HighPart = pFileData->ftLastWriteTime.dwHighDateTime;
2041+
basic_info->LastWriteTime.LowPart = pFileData->ftLastWriteTime.dwLowDateTime;
2042+
standard_info->EndOfFile.HighPart = pFileData->nFileSizeHigh;
2043+
standard_info->EndOfFile.LowPart = pFileData->nFileSizeLow;
2044+
20392045
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
20402046
*reparse_tag = pFileData->dwReserved0;
20412047
else
20422048
*reparse_tag = 0;
20432049
}
20442050

20452051
static BOOL
2046-
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag)
2052+
attributes_from_dir(LPCWSTR pszFile, FILE_BASIC_INFO* basic_info, FILE_STANDARD_INFO* standard_info, ULONG* reparse_tag)
20472053
{
20482054
HANDLE hFindFile;
20492055
WIN32_FIND_DATAW FileData;
@@ -2074,7 +2080,7 @@ attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *re
20742080
return FALSE;
20752081
}
20762082
FindClose(hFindFile);
2077-
find_data_to_file_info(&FileData, info, reparse_tag);
2083+
find_data_to_file_info(&FileData, basic_info, standard_info, reparse_tag);
20782084
return TRUE;
20792085
}
20802086

@@ -2108,10 +2114,9 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21082114
BOOL traverse)
21092115
{
21102116
HANDLE hFile;
2111-
BY_HANDLE_FILE_INFORMATION fileInfo;
2112-
FILE_BASIC_INFO basicInfo;
2113-
FILE_BASIC_INFO *pBasicInfo = NULL;
2114-
FILE_ID_INFO idInfo;
2117+
FILE_STANDARD_INFO standardInfo = {0};
2118+
FILE_BASIC_INFO basicInfo = {0};
2119+
FILE_ID_INFO idInfo = {0};
21152120
FILE_ID_INFO *pIdInfo = NULL;
21162121
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
21172122
DWORD fileType, error;
@@ -2132,7 +2137,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21322137
case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */
21332138
case ERROR_SHARING_VIOLATION: /* It's a paging file. */
21342139
/* Try reading the parent directory. */
2135-
if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) {
2140+
if (!attributes_from_dir(path, &basicInfo, &standardInfo, &tagInfo.ReparseTag)) {
21362141
/* Cannot read the parent directory. */
21372142
switch (GetLastError()) {
21382143
case ERROR_FILE_NOT_FOUND: /* File cannot be found */
@@ -2147,7 +2152,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21472152

21482153
return -1;
21492154
}
2150-
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2155+
if (basicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
21512156
if (traverse ||
21522157
!IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
21532158
/* The stat call has to traverse but cannot, so fail. */
@@ -2247,9 +2252,8 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22472252
}
22482253
}
22492254

2250-
if (!GetFileInformationByHandle(hFile, &fileInfo) ||
2251-
!GetFileInformationByHandleEx(hFile, FileBasicInfo,
2252-
&basicInfo, sizeof(basicInfo))) {
2255+
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
2256+
!GetFileInformationByHandleEx(hFile, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
22532257
switch (GetLastError()) {
22542258
case ERROR_INVALID_PARAMETER:
22552259
case ERROR_INVALID_FUNCTION:
@@ -2264,17 +2268,14 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22642268
goto cleanup;
22652269
}
22662270

2267-
/* Successfully got FileBasicInfo, so we'll pass it along */
2268-
pBasicInfo = &basicInfo;
2269-
22702271
if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
22712272
/* Successfully got FileIdInfo, so pass it along */
22722273
pIdInfo = &idInfo;
22732274
}
22742275
}
22752276

2276-
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result);
2277-
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
2277+
_Py_attribute_data_to_stat(&standardInfo, tagInfo.ReparseTag, &basicInfo, pIdInfo, result);
2278+
update_st_mode_from_path(path, basicInfo.FileAttributes, result);
22782279

22792280
cleanup:
22802281
if (hFile != INVALID_HANDLE_VALUE) {
@@ -16702,7 +16703,8 @@ static PyObject *
1670216703
DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1670316704
{
1670416705
DirEntry *entry;
16705-
BY_HANDLE_FILE_INFORMATION file_info;
16706+
FILE_BASIC_INFO basic_info;
16707+
FILE_STANDARD_INFO standard_info;
1670616708
ULONG reparse_tag;
1670716709
wchar_t *joined_path;
1670816710

@@ -16740,8 +16742,8 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1674016742
goto error;
1674116743
}
1674216744

16743-
find_data_to_file_info(dataW, &file_info, &reparse_tag);
16744-
_Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);
16745+
find_data_to_file_info(dataW, &basic_info, &standard_info, &reparse_tag);
16746+
_Py_attribute_data_to_stat(&standard_info, reparse_tag, &basic_info, NULL, &entry->win32_lstat);
1674516747

1674616748
/* ctime is only deprecated from 3.12, so we copy birthtime across */
1674716749
entry->win32_lstat.st_ctime = entry->win32_lstat.st_birthtime;

Python/fileutils.c

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,50 +1103,41 @@ typedef union {
11031103

11041104

11051105
void
1106-
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
1106+
_Py_attribute_data_to_stat(FILE_STANDARD_INFO* standard_info, ULONG reparse_tag,
11071107
FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
11081108
struct _Py_stat_struct *result)
11091109
{
11101110
memset(result, 0, sizeof(*result));
1111-
result->st_mode = attributes_to_mode(info->dwFileAttributes);
1112-
result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
1113-
result->st_dev = id_info ? id_info->VolumeSerialNumber : info->dwVolumeSerialNumber;
1114-
result->st_rdev = 0;
1111+
1112+
result->st_size = standard_info->EndOfFile.QuadPart;
1113+
result->st_nlink = standard_info->NumberOfLinks;
1114+
11151115
/* st_ctime is deprecated, but we preserve the legacy value in our caller, not here */
1116-
if (basic_info) {
1117-
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
1118-
LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
1119-
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1120-
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
1121-
} else {
1122-
FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
1123-
FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1124-
FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
1125-
}
1126-
result->st_nlink = info->nNumberOfLinks;
1116+
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
1117+
LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
1118+
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1119+
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
11271120

11281121
if (id_info) {
1122+
result->st_dev = id_info->VolumeSerialNumber;
11291123
id_128_to_ino file_id;
11301124
file_id.id = id_info->FileId;
11311125
result->st_ino = file_id.st_ino;
11321126
result->st_ino_high = file_id.st_ino_high;
11331127
}
1134-
if (!result->st_ino && !result->st_ino_high) {
1135-
/* should only occur for DirEntry_from_find_data, in which case the
1136-
index is likely to be zero anyway. */
1137-
result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
1138-
}
1128+
1129+
result->st_file_attributes = basic_info->FileAttributes;
1130+
result->st_mode = attributes_to_mode(result->st_file_attributes);
11391131

11401132
/* bpo-37834: Only actual symlinks set the S_IFLNK flag. But lstat() will
11411133
open other name surrogate reparse points without traversing them. To
11421134
detect/handle these, check st_file_attributes and st_reparse_tag. */
11431135
result->st_reparse_tag = reparse_tag;
1144-
if (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1136+
if (result->st_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
11451137
reparse_tag == IO_REPARSE_TAG_SYMLINK) {
11461138
/* set the bits that make this a symlink */
11471139
result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
11481140
}
1149-
result->st_file_attributes = info->dwFileAttributes;
11501141
}
11511142

11521143
void
@@ -1231,9 +1222,9 @@ int
12311222
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12321223
{
12331224
#ifdef MS_WINDOWS
1234-
BY_HANDLE_FILE_INFORMATION info;
1235-
FILE_BASIC_INFO basicInfo;
1236-
FILE_ID_INFO idInfo;
1225+
FILE_STANDARD_INFO standardInfo = {0};
1226+
FILE_BASIC_INFO basicInfo = {0};
1227+
FILE_ID_INFO idInfo = {0};
12371228
FILE_ID_INFO *pIdInfo = &idInfo;
12381229
HANDLE h;
12391230
int type;
@@ -1266,7 +1257,7 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12661257
return 0;
12671258
}
12681259

1269-
if (!GetFileInformationByHandle(h, &info) ||
1260+
if (!GetFileInformationByHandleEx(h,FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
12701261
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
12711262
/* The Win32 error is already set, but we also set errno for
12721263
callers who expect it */
@@ -1279,7 +1270,7 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12791270
pIdInfo = NULL;
12801271
}
12811272

1282-
_Py_attribute_data_to_stat(&info, 0, &basicInfo, pIdInfo, status);
1273+
_Py_attribute_data_to_stat(&standardInfo, 0, &basicInfo, pIdInfo, status);
12831274
return 0;
12841275
#else
12851276
return fstat(fd, status);

0 commit comments

Comments
 (0)