Skip to content

Commit 190b3f2

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

3 files changed

Lines changed: 106 additions & 23 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: 39 additions & 20 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

@@ -2043,7 +2046,7 @@ find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
20432046
}
20442047

20452048
static BOOL
2046-
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag)
2049+
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION* info, ULONG *reparse_tag, struct _Py_stat_struct* result)
20472050
{
20482051
HANDLE hFindFile;
20492052
WIN32_FIND_DATAW FileData;
@@ -2074,7 +2077,12 @@ attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *re
20742077
return FALSE;
20752078
}
20762079
FindClose(hFindFile);
2080+
2081+
#ifdef MS_WINDOWS_DESKTOP
20772082
find_data_to_file_info(&FileData, info, reparse_tag);
2083+
#else
2084+
_Py_find_data_to_stat(&FileData, result);
2085+
#endif
20782086
return TRUE;
20792087
}
20802088

@@ -2108,10 +2116,10 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21082116
BOOL traverse)
21092117
{
21102118
HANDLE hFile;
2111-
BY_HANDLE_FILE_INFORMATION fileInfo;
2112-
FILE_BASIC_INFO basicInfo;
2113-
FILE_BASIC_INFO *pBasicInfo = NULL;
2114-
FILE_ID_INFO idInfo;
2119+
BY_HANDLE_FILE_INFORMATION fileInfo = {0};
2120+
FILE_STANDARD_INFO standardInfo = {0};
2121+
FILE_BASIC_INFO basicInfo = {0};
2122+
FILE_ID_INFO idInfo = {0};
21152123
FILE_ID_INFO *pIdInfo = NULL;
21162124
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
21172125
DWORD fileType, error;
@@ -2132,7 +2140,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21322140
case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */
21332141
case ERROR_SHARING_VIOLATION: /* It's a paging file. */
21342142
/* Try reading the parent directory. */
2135-
if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) {
2143+
if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag, result)) {
21362144
/* Cannot read the parent directory. */
21372145
switch (GetLastError()) {
21382146
case ERROR_FILE_NOT_FOUND: /* File cannot be found */
@@ -2147,7 +2155,12 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21472155

21482156
return -1;
21492157
}
2158+
2159+
#ifdef MS_WINDOWS_DESKTOP
21502160
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2161+
#else
2162+
if (result->st_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2163+
#endif
21512164
if (traverse ||
21522165
!IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
21532166
/* The stat call has to traverse but cannot, so fail. */
@@ -2247,9 +2260,13 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22472260
}
22482261
}
22492262

2263+
#ifdef MS_WINDOWS_DESKTOP
22502264
if (!GetFileInformationByHandle(hFile, &fileInfo) ||
2251-
!GetFileInformationByHandleEx(hFile, FileBasicInfo,
2252-
&basicInfo, sizeof(basicInfo))) {
2265+
!GetFileInformationByHandleEx(hFile, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
2266+
#else
2267+
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
2268+
!GetFileInformationByHandleEx(hFile, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
2269+
#endif
22532270
switch (GetLastError()) {
22542271
case ERROR_INVALID_PARAMETER:
22552272
case ERROR_INVALID_FUNCTION:
@@ -2264,17 +2281,18 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22642281
goto cleanup;
22652282
}
22662283

2267-
/* Successfully got FileBasicInfo, so we'll pass it along */
2268-
pBasicInfo = &basicInfo;
2269-
22702284
if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
22712285
/* Successfully got FileIdInfo, so pass it along */
22722286
pIdInfo = &idInfo;
22732287
}
22742288
}
22752289

2276-
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result);
2277-
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
2290+
#ifdef MS_WINDOWS_DESKTOP
2291+
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, &basicInfo, pIdInfo, result);
2292+
#else
2293+
_Py_attribute_data_to_stat_UWP(&standardInfo, tagInfo.ReparseTag, &basicInfo, result);
2294+
#endif
2295+
update_st_mode_from_path(path, basicInfo.FileAttributes, result);
22782296

22792297
cleanup:
22802298
if (hFile != INVALID_HANDLE_VALUE) {
@@ -16669,13 +16687,8 @@ join_path_filenameW(const wchar_t *path_wide, const wchar_t *filename,
1666916687
static PyObject *
1667016688
DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1667116689
{
16672-
DirEntry *entry;
16673-
BY_HANDLE_FILE_INFORMATION file_info;
16674-
ULONG reparse_tag;
16675-
wchar_t *joined_path;
16676-
1667716690
PyObject *DirEntryType = get_posix_state(module)->DirEntryType;
16678-
entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType);
16691+
DirEntry* entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType);
1667916692
if (!entry)
1668016693
return NULL;
1668116694
entry->name = NULL;
@@ -16694,7 +16707,7 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1669416707
goto error;
1669516708
}
1669616709

16697-
joined_path = join_path_filenameW(path->wide, dataW->cFileName, 0);
16710+
wchar_t* joined_path = join_path_filenameW(path->wide, dataW->cFileName, 0);
1669816711
if (!joined_path)
1669916712
goto error;
1670016713

@@ -16708,8 +16721,14 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1670816721
goto error;
1670916722
}
1671016723

16724+
#ifdef MS_WINDOWS_DESKTOP
16725+
BY_HANDLE_FILE_INFORMATION file_info;
16726+
ULONG reparse_tag;
1671116727
find_data_to_file_info(dataW, &file_info, &reparse_tag);
1671216728
_Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);
16729+
#else
16730+
_Py_find_data_to_stat(dataW, &entry->win32_lstat);
16731+
#endif
1671316732

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

Python/fileutils.c

Lines changed: 66 additions & 3 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,29 @@ _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+
result->st_reparse_tag = find_data->dwReserved0;
1258+
1259+
if (find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1260+
find_data->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
1261+
/* first clear the S_IFMT bits */
1262+
result->st_mode ^= (result->st_mode & S_IFMT);
1263+
/* now set the bits that make this a symlink */
1264+
result->st_mode |= S_IFLNK;
1265+
}
1266+
result->st_file_attributes = find_data->dwFileAttributes;
1267+
}
1268+
12161269
#endif
12171270

12181271
/* Return information about a file.
@@ -1231,9 +1284,10 @@ int
12311284
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12321285
{
12331286
#ifdef MS_WINDOWS
1234-
BY_HANDLE_FILE_INFORMATION info;
1235-
FILE_BASIC_INFO basicInfo;
1236-
FILE_ID_INFO idInfo;
1287+
BY_HANDLE_FILE_INFORMATION info = {0};
1288+
FILE_STANDARD_INFO standardInfo = {0};
1289+
FILE_BASIC_INFO basicInfo = {0};
1290+
FILE_ID_INFO idInfo = {0};
12371291
FILE_ID_INFO *pIdInfo = &idInfo;
12381292
HANDLE h;
12391293
int type;
@@ -1266,8 +1320,13 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12661320
return 0;
12671321
}
12681322

1323+
#ifdef MS_WINDOWS_DESKTOP
12691324
if (!GetFileInformationByHandle(h, &info) ||
12701325
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
1326+
#else
1327+
if (!GetFileInformationByHandleEx(h,FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
1328+
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
1329+
#endif
12711330
/* The Win32 error is already set, but we also set errno for
12721331
callers who expect it */
12731332
errno = winerror_to_errno(GetLastError());
@@ -1279,7 +1338,11 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12791338
pIdInfo = NULL;
12801339
}
12811340

1341+
#ifdef MS_WINDOWS_DESKTOP
12821342
_Py_attribute_data_to_stat(&info, 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)