Skip to content

Commit 57f8125

Browse files
committed
gh-152433: Windows: modernize fileutils to allow build for Windows UWP
1 parent 5b85756 commit 57f8125

3 files changed

Lines changed: 97 additions & 44 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Modernize fileutils to allow build for Windows UWP.

Modules/posixmodule.c

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,9 @@ void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG,
834834
struct _Py_stat_struct *);
835835
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *,
836836
struct _Py_stat_struct *);
837+
void _Py_attribute_data_to_stat_UWP(FILE_STANDARD_INFO*, ULONG,
838+
FILE_BASIC_INFO*, struct _Py_stat_struct*);
839+
void _Py_find_data_to_stat(WIN32_FIND_DATAW* find_data, struct _Py_stat_struct* result);
837840
#endif
838841

839842

@@ -2023,27 +2026,8 @@ win32_wchdir(LPCWSTR path)
20232026
#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1
20242027
#define HAVE_STRUCT_STAT_ST_REPARSE_TAG 1
20252028

2026-
static void
2027-
find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
2028-
BY_HANDLE_FILE_INFORMATION *info,
2029-
ULONG *reparse_tag)
2030-
{
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; */
2039-
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
2040-
*reparse_tag = pFileData->dwReserved0;
2041-
else
2042-
*reparse_tag = 0;
2043-
}
2044-
20452029
static BOOL
2046-
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag)
2030+
attributes_from_dir(LPCWSTR pszFile, struct _Py_stat_struct* result, ULONG *reparse_tag)
20472031
{
20482032
HANDLE hFindFile;
20492033
WIN32_FIND_DATAW FileData;
@@ -2074,7 +2058,7 @@ attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *re
20742058
return FALSE;
20752059
}
20762060
FindClose(hFindFile);
2077-
find_data_to_file_info(&FileData, info, reparse_tag);
2061+
_Py_find_data_to_stat(&FileData, result);
20782062
return TRUE;
20792063
}
20802064

@@ -2108,10 +2092,11 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21082092
BOOL traverse)
21092093
{
21102094
HANDLE hFile;
2111-
BY_HANDLE_FILE_INFORMATION fileInfo;
2112-
FILE_BASIC_INFO basicInfo;
2113-
FILE_BASIC_INFO *pBasicInfo = NULL;
2114-
FILE_ID_INFO idInfo;
2095+
BY_HANDLE_FILE_INFORMATION fileInfo = {0};
2096+
BY_HANDLE_FILE_INFORMATION* pFileInfo = NULL;
2097+
FILE_STANDARD_INFO standardInfo = {0};
2098+
FILE_BASIC_INFO basicInfo = {0};
2099+
FILE_ID_INFO idInfo = {0};
21152100
FILE_ID_INFO *pIdInfo = NULL;
21162101
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
21172102
DWORD fileType, error;
@@ -2132,7 +2117,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21322117
case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */
21332118
case ERROR_SHARING_VIOLATION: /* It's a paging file. */
21342119
/* Try reading the parent directory. */
2135-
if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) {
2120+
if (!attributes_from_dir(path, result, &tagInfo.ReparseTag)) {
21362121
/* Cannot read the parent directory. */
21372122
switch (GetLastError()) {
21382123
case ERROR_FILE_NOT_FOUND: /* File cannot be found */
@@ -2147,7 +2132,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21472132

21482133
return -1;
21492134
}
2150-
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2135+
if (result->st_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
21512136
if (traverse ||
21522137
!IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
21532138
/* The stat call has to traverse but cannot, so fail. */
@@ -2247,9 +2232,15 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22472232
}
22482233
}
22492234

2250-
if (!GetFileInformationByHandle(hFile, &fileInfo) ||
2251-
!GetFileInformationByHandleEx(hFile, FileBasicInfo,
2252-
&basicInfo, sizeof(basicInfo))) {
2235+
#ifdef MS_WINDOWS_DESKTOP
2236+
if (GetFileInformationByHandle(hFile, &fileInfo)) {
2237+
/* Successfully got fileInfo, so pass it along */
2238+
pFileInfo = &fileInfo;
2239+
}
2240+
#endif
2241+
2242+
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
2243+
!GetFileInformationByHandleEx(hFile, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
22532244
switch (GetLastError()) {
22542245
case ERROR_INVALID_PARAMETER:
22552246
case ERROR_INVALID_FUNCTION:
@@ -2264,17 +2255,18 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22642255
goto cleanup;
22652256
}
22662257

2267-
/* Successfully got FileBasicInfo, so we'll pass it along */
2268-
pBasicInfo = &basicInfo;
2269-
22702258
if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
22712259
/* Successfully got FileIdInfo, so pass it along */
22722260
pIdInfo = &idInfo;
22732261
}
22742262
}
22752263

2276-
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result);
2277-
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
2264+
#ifdef MS_WINDOWS_DESKTOP
2265+
_Py_attribute_data_to_stat(pFileInfo, tagInfo.ReparseTag, &basicInfo, pIdInfo, result);
2266+
#else
2267+
_Py_attribute_data_to_stat_UWP(&standardInfo, tagInfo.ReparseTag, &basicInfo, result);
2268+
#endif
2269+
update_st_mode_from_path(path, basicInfo.FileAttributes, result);
22782270

22792271
cleanup:
22802272
if (hFile != INVALID_HANDLE_VALUE) {
@@ -16670,8 +16662,6 @@ static PyObject *
1667016662
DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1667116663
{
1667216664
DirEntry *entry;
16673-
BY_HANDLE_FILE_INFORMATION file_info;
16674-
ULONG reparse_tag;
1667516665
wchar_t *joined_path;
1667616666

1667716667
PyObject *DirEntryType = get_posix_state(module)->DirEntryType;
@@ -16708,8 +16698,7 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1670816698
goto error;
1670916699
}
1671016700

16711-
find_data_to_file_info(dataW, &file_info, &reparse_tag);
16712-
_Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);
16701+
_Py_find_data_to_stat(dataW, &entry->win32_lstat);
1671316702

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

Python/fileutils.c

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,36 @@ _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
11491149
result->st_file_attributes = info->dwFileAttributes;
11501150
}
11511151

1152+
void
1153+
_Py_attribute_data_to_stat_UWP(FILE_STANDARD_INFO* standard_info, ULONG reparse_tag,
1154+
FILE_BASIC_INFO* basic_info, struct _Py_stat_struct* result)
1155+
{
1156+
memset(result, 0, sizeof(*result));
1157+
result->st_mode = attributes_to_mode(basic_info->FileAttributes);
1158+
result->st_size = standard_info->EndOfFile.QuadPart;
1159+
result->st_dev = 1;
1160+
1161+
/* st_ctime is deprecated, but we preserve the legacy value in our caller, not here */
1162+
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
1163+
LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
1164+
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1165+
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
1166+
1167+
result->st_nlink = standard_info->NumberOfLinks;
1168+
result->st_ino = basic_info->CreationTime.QuadPart;
1169+
1170+
/* bpo-37834: Only actual symlinks set the S_IFLNK flag. But lstat() will
1171+
open other name surrogate reparse points without traversing them. To
1172+
detect/handle these, check st_file_attributes and st_reparse_tag. */
1173+
result->st_reparse_tag = reparse_tag;
1174+
if (basic_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1175+
reparse_tag == IO_REPARSE_TAG_SYMLINK) {
1176+
/* set the bits that make this a symlink */
1177+
result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
1178+
}
1179+
result->st_file_attributes = basic_info->FileAttributes;
1180+
}
1181+
11521182
void
11531183
_Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *info,
11541184
struct _Py_stat_struct *result)
@@ -1213,6 +1243,26 @@ _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *info,
12131243
}
12141244
}
12151245

1246+
void
1247+
_Py_find_data_to_stat(WIN32_FIND_DATAW* find_data, struct _Py_stat_struct* result)
1248+
{
1249+
memset(result, 0, sizeof(*result));
1250+
result->st_mode = attributes_to_mode(find_data->dwFileAttributes);
1251+
FILE_TIME_to_time_t_nsec(&find_data->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
1252+
FILE_TIME_to_time_t_nsec(&find_data->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1253+
FILE_TIME_to_time_t_nsec(&find_data->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
1254+
result->st_size = (((long long)find_data->nFileSizeHigh) << 32) | find_data->nFileSizeLow;
1255+
1256+
if (find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1257+
find_data->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
1258+
/* first clear the S_IFMT bits */
1259+
result->st_mode ^= (result->st_mode & S_IFMT);
1260+
/* now set the bits that make this a symlink */
1261+
result->st_mode |= S_IFLNK;
1262+
}
1263+
result->st_file_attributes = find_data->dwFileAttributes;
1264+
}
1265+
12161266
#endif
12171267

12181268
/* Return information about a file.
@@ -1231,9 +1281,11 @@ int
12311281
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12321282
{
12331283
#ifdef MS_WINDOWS
1234-
BY_HANDLE_FILE_INFORMATION info;
1235-
FILE_BASIC_INFO basicInfo;
1236-
FILE_ID_INFO idInfo;
1284+
BY_HANDLE_FILE_INFORMATION fileInfo = {0};
1285+
BY_HANDLE_FILE_INFORMATION* pFileInfo = NULL;
1286+
FILE_STANDARD_INFO standardInfo = {0};
1287+
FILE_BASIC_INFO basicInfo = {0};
1288+
FILE_ID_INFO idInfo = {0};
12371289
FILE_ID_INFO *pIdInfo = &idInfo;
12381290
HANDLE h;
12391291
int type;
@@ -1266,7 +1318,14 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12661318
return 0;
12671319
}
12681320

1269-
if (!GetFileInformationByHandle(h, &info) ||
1321+
#ifdef MS_WINDOWS_DESKTOP
1322+
if (GetFileInformationByHandle(h, &fileInfo)) {
1323+
/* Successfully got fileInfo, so pass it along */
1324+
pFileInfo = &fileInfo;
1325+
}
1326+
#endif
1327+
1328+
if (!GetFileInformationByHandleEx(h,FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
12701329
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
12711330
/* The Win32 error is already set, but we also set errno for
12721331
callers who expect it */
@@ -1279,7 +1338,11 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12791338
pIdInfo = NULL;
12801339
}
12811340

1282-
_Py_attribute_data_to_stat(&info, 0, &basicInfo, pIdInfo, status);
1341+
#ifdef MS_WINDOWS_DESKTOP
1342+
_Py_attribute_data_to_stat(pFileInfo, 0, &basicInfo, pIdInfo, status);
1343+
#else
1344+
_Py_attribute_data_to_stat_UWP(&standardInfo, 0, &basicInfo, status);
1345+
#endif
12831346
return 0;
12841347
#else
12851348
return fstat(fd, status);

0 commit comments

Comments
 (0)