Skip to content

Commit a26c789

Browse files
committed
gh-152433: Windows: modernize and refactor fileutils to allow build for Windows UWP
1 parent 050a84b commit a26c789

2 files changed

Lines changed: 92 additions & 101 deletions

File tree

Modules/posixmodule.c

Lines changed: 17 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -828,12 +828,15 @@ PyOS_AfterFork(void)
828828

829829
#ifdef MS_WINDOWS
830830
/* defined in fileutils.c */
831-
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 *);
835-
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *,
836-
struct _Py_stat_struct *);
831+
void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME*);
832+
void _Py_attribute_data_to_stat(FILE_BASIC_INFO* basic_info,
833+
FILE_STANDARD_INFO* standard_info,
834+
FILE_ID_INFO* id_info,
835+
ULONG reparse_tag,
836+
struct _Py_stat_struct* result);
837+
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION*, struct _Py_stat_struct*);
838+
void _Py_find_data_to_stat(WIN32_FIND_DATAW*, struct _Py_stat_struct*);
839+
int _Py_stat_from_file_handle(HANDLE h, struct _Py_stat_struct* result, ULONG reparse_tag, BOOL set_ino);
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

@@ -2083,7 +2067,7 @@ static void
20832067
update_st_mode_from_path(const wchar_t *path, DWORD attr,
20842068
struct _Py_stat_struct *result)
20852069
{
2086-
if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
2070+
if (!(attr & _S_IFDIR)) {
20872071
/* Fix the file execute permissions. This hack sets S_IEXEC if
20882072
the filename has an extension that is commonly used by files
20892073
that CreateProcessW can execute. A real implementation calls
@@ -2108,11 +2092,6 @@ 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;
2115-
FILE_ID_INFO *pIdInfo = NULL;
21162095
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
21172096
DWORD fileType, error;
21182097
BOOL isUnhandledTag = FALSE;
@@ -2132,7 +2111,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21322111
case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */
21332112
case ERROR_SHARING_VIOLATION: /* It's a paging file. */
21342113
/* Try reading the parent directory. */
2135-
if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) {
2114+
if (!attributes_from_dir(path, result, &tagInfo.ReparseTag)) {
21362115
/* Cannot read the parent directory. */
21372116
switch (GetLastError()) {
21382117
case ERROR_FILE_NOT_FOUND: /* File cannot be found */
@@ -2147,7 +2126,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
21472126

21482127
return -1;
21492128
}
2150-
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2129+
if (result->st_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
21512130
if (traverse ||
21522131
!IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
21532132
/* The stat call has to traverse but cannot, so fail. */
@@ -2247,34 +2226,13 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
22472226
}
22482227
}
22492228

2250-
if (!GetFileInformationByHandle(hFile, &fileInfo) ||
2251-
!GetFileInformationByHandleEx(hFile, FileBasicInfo,
2252-
&basicInfo, sizeof(basicInfo))) {
2253-
switch (GetLastError()) {
2254-
case ERROR_INVALID_PARAMETER:
2255-
case ERROR_INVALID_FUNCTION:
2256-
case ERROR_NOT_SUPPORTED:
2257-
/* Volumes and physical disks are block devices, e.g.
2258-
\\.\C: and \\.\PhysicalDrive0. */
2259-
memset(result, 0, sizeof(*result));
2260-
result->st_mode = 0x6000; /* S_IFBLK */
2261-
goto cleanup;
2262-
}
2229+
if (_Py_stat_from_file_handle(hFile, result, tagInfo.ReparseTag, FALSE)) {
22632230
retval = -1;
22642231
goto cleanup;
22652232
}
2266-
2267-
/* Successfully got FileBasicInfo, so we'll pass it along */
2268-
pBasicInfo = &basicInfo;
2269-
2270-
if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
2271-
/* Successfully got FileIdInfo, so pass it along */
2272-
pIdInfo = &idInfo;
2273-
}
22742233
}
22752234

2276-
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result);
2277-
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
2235+
update_st_mode_from_path(path, result->st_mode, result);
22782236

22792237
cleanup:
22802238
if (hFile != INVALID_HANDLE_VALUE) {
@@ -16667,8 +16625,6 @@ static PyObject *
1666716625
DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1666816626
{
1666916627
DirEntry *entry;
16670-
BY_HANDLE_FILE_INFORMATION file_info;
16671-
ULONG reparse_tag;
1667216628
wchar_t *joined_path;
1667316629

1667416630
PyObject *DirEntryType = get_posix_state(module)->DirEntryType;
@@ -16705,8 +16661,7 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
1670516661
goto error;
1670616662
}
1670716663

16708-
find_data_to_file_info(dataW, &file_info, &reparse_tag);
16709-
_Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);
16664+
_Py_find_data_to_stat(dataW, &entry->win32_lstat);
1671016665

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

Python/fileutils.c

Lines changed: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,29 +1103,26 @@ typedef union {
11031103

11041104

11051105
void
1106-
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
1107-
FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
1108-
struct _Py_stat_struct *result)
1106+
_Py_attribute_data_to_stat(FILE_BASIC_INFO* basic_info,
1107+
FILE_STANDARD_INFO* standard_info,
1108+
FILE_ID_INFO* id_info,
1109+
ULONG reparse_tag,
1110+
struct _Py_stat_struct* result)
11091111
{
11101112
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;
1113+
result->st_mode = attributes_to_mode(basic_info->FileAttributes);
1114+
result->st_size = standard_info->EndOfFile.QuadPart;
11141115
result->st_rdev = 0;
1116+
11151117
/* 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;
1118+
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
1119+
LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
1120+
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1121+
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
1122+
result->st_nlink = standard_info->NumberOfLinks;
11271123

11281124
if (id_info) {
1125+
result->st_dev = id_info->VolumeSerialNumber;
11291126
id_128_to_ino file_id;
11301127
file_id.id = id_info->FileId;
11311128
result->st_ino = file_id.st_ino;
@@ -1134,19 +1131,19 @@ _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
11341131
if (!result->st_ino && !result->st_ino_high) {
11351132
/* should only occur for DirEntry_from_find_data, in which case the
11361133
index is likely to be zero anyway. */
1137-
result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
1134+
result->st_ino = basic_info->CreationTime.QuadPart;
11381135
}
11391136

11401137
/* bpo-37834: Only actual symlinks set the S_IFLNK flag. But lstat() will
11411138
open other name surrogate reparse points without traversing them. To
11421139
detect/handle these, check st_file_attributes and st_reparse_tag. */
11431140
result->st_reparse_tag = reparse_tag;
1144-
if (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1141+
if (basic_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
11451142
reparse_tag == IO_REPARSE_TAG_SYMLINK) {
11461143
/* set the bits that make this a symlink */
11471144
result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
11481145
}
1149-
result->st_file_attributes = info->dwFileAttributes;
1146+
result->st_file_attributes = basic_info->FileAttributes;
11501147
}
11511148

11521149
void
@@ -1213,6 +1210,63 @@ _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *info,
12131210
}
12141211
}
12151212

1213+
void
1214+
_Py_find_data_to_stat(WIN32_FIND_DATAW* find_data, struct _Py_stat_struct* result)
1215+
{
1216+
memset(result, 0, sizeof(*result));
1217+
result->st_mode = attributes_to_mode(find_data->dwFileAttributes);
1218+
FILE_TIME_to_time_t_nsec(&find_data->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
1219+
FILE_TIME_to_time_t_nsec(&find_data->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1220+
FILE_TIME_to_time_t_nsec(&find_data->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
1221+
result->st_size = ((long long)find_data->nFileSizeHigh) << 32 > find_data->nFileSizeLow;
1222+
1223+
if (find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1224+
find_data->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
1225+
/* first clear the S_IFMT bits */
1226+
result->st_mode ^= (result->st_mode & S_IFMT);
1227+
/* now set the bits that make this a symlink */
1228+
result->st_mode |= S_IFLNK;
1229+
}
1230+
result->st_file_attributes = find_data->dwFileAttributes;
1231+
}
1232+
1233+
int
1234+
_Py_stat_from_file_handle(HANDLE h, struct _Py_stat_struct* result, ULONG reparse_tag, BOOL set_ino)
1235+
{
1236+
FILE_BASIC_INFO basic_info = {0};
1237+
FILE_STANDARD_INFO standard_info = {0};
1238+
FILE_ID_INFO idInfo = {0};
1239+
FILE_ID_INFO* pIdInfo = &idInfo;
1240+
if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic_info, sizeof(basic_info)) ||
1241+
!GetFileInformationByHandleEx(h, FileStandardInfo, &standard_info, sizeof(standard_info))) {
1242+
/* The Win32 error is already set, but we also set errno for
1243+
callers who expect it */
1244+
switch (GetLastError()) {
1245+
case ERROR_INVALID_PARAMETER:
1246+
case ERROR_INVALID_FUNCTION:
1247+
case ERROR_NOT_SUPPORTED:
1248+
/* Volumes and physical disks are block devices, e.g.
1249+
\\.\C: and \\.\PhysicalDrive0. */
1250+
memset(result, 0, sizeof(*result));
1251+
result->st_mode = 0x6000; /* S_IFBLK */
1252+
}
1253+
PyErr_SetFromWindowsErr(0);
1254+
errno = winerror_to_errno(GetLastError());
1255+
return -1;
1256+
}
1257+
1258+
if (!GetFileInformationByHandleEx(h, FileIdInfo, &idInfo, sizeof(idInfo))) {
1259+
/* Failed to get FileIdInfo, so do not pass it along */
1260+
pIdInfo = NULL;
1261+
}
1262+
1263+
_Py_attribute_data_to_stat(&basic_info, &standard_info, pIdInfo, reparse_tag, result);
1264+
/* specific to fstat() */
1265+
if (set_ino) {
1266+
result->st_ino = basic_info.CreationTime.QuadPart;
1267+
}
1268+
return 0;
1269+
}
12161270
#endif
12171271

12181272
/* Return information about a file.
@@ -1231,10 +1285,6 @@ int
12311285
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12321286
{
12331287
#ifdef MS_WINDOWS
1234-
BY_HANDLE_FILE_INFORMATION info;
1235-
FILE_BASIC_INFO basicInfo;
1236-
FILE_ID_INFO idInfo;
1237-
FILE_ID_INFO *pIdInfo = &idInfo;
12381288
HANDLE h;
12391289
int type;
12401290

@@ -1266,21 +1316,7 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
12661316
return 0;
12671317
}
12681318

1269-
if (!GetFileInformationByHandle(h, &info) ||
1270-
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
1271-
/* The Win32 error is already set, but we also set errno for
1272-
callers who expect it */
1273-
errno = winerror_to_errno(GetLastError());
1274-
return -1;
1275-
}
1276-
1277-
if (!GetFileInformationByHandleEx(h, FileIdInfo, &idInfo, sizeof(idInfo))) {
1278-
/* Failed to get FileIdInfo, so do not pass it along */
1279-
pIdInfo = NULL;
1280-
}
1281-
1282-
_Py_attribute_data_to_stat(&info, 0, &basicInfo, pIdInfo, status);
1283-
return 0;
1319+
return _Py_stat_from_file_handle(h, status, 0, TRUE);
12841320
#else
12851321
return fstat(fd, status);
12861322
#endif

0 commit comments

Comments
 (0)