Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Modernize fileutils to allow build for Windows UWP.
26 changes: 11 additions & 15 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,8 +829,8 @@ PyOS_AfterFork(void)
#ifdef MS_WINDOWS
/* defined in fileutils.c */
void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG,
FILE_BASIC_INFO *, FILE_ID_INFO *,
void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, FILE_STANDARD_INFO*,
ULONG, FILE_BASIC_INFO *, FILE_ID_INFO *,
struct _Py_stat_struct *);
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *,
struct _Py_stat_struct *);
Expand Down Expand Up @@ -2108,10 +2108,10 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
BOOL traverse)
{
HANDLE hFile;
BY_HANDLE_FILE_INFORMATION fileInfo;
FILE_BASIC_INFO basicInfo;
FILE_BASIC_INFO *pBasicInfo = NULL;
FILE_ID_INFO idInfo;
BY_HANDLE_FILE_INFORMATION fileInfo = {0};
FILE_STANDARD_INFO standardInfo = {0};
FILE_BASIC_INFO basicInfo = {0};
FILE_ID_INFO idInfo = {0};
FILE_ID_INFO *pIdInfo = NULL;
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
DWORD fileType, error;
Expand Down Expand Up @@ -2247,9 +2247,8 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
}
}

if (!GetFileInformationByHandle(hFile, &fileInfo) ||
!GetFileInformationByHandleEx(hFile, FileBasicInfo,
&basicInfo, sizeof(basicInfo))) {
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, sizeof(standardInfo)) ||
Comment thread
thexai marked this conversation as resolved.
Resolved
!GetFileInformationByHandleEx(hFile, FileBasicInfo, &basicInfo, sizeof(basicInfo))) {
switch (GetLastError()) {
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
Expand All @@ -2264,17 +2263,14 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
goto cleanup;
}

/* Successfully got FileBasicInfo, so we'll pass it along */
pBasicInfo = &basicInfo;

if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
/* Successfully got FileIdInfo, so pass it along */
pIdInfo = &idInfo;
}
}

_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result);
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
_Py_attribute_data_to_stat(NULL, &standardInfo, tagInfo.ReparseTag, &basicInfo, pIdInfo, result);
update_st_mode_from_path(path, basicInfo.FileAttributes, result);

cleanup:
if (hFile != INVALID_HANDLE_VALUE) {
Expand Down Expand Up @@ -16709,7 +16705,7 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
}

find_data_to_file_info(dataW, &file_info, &reparse_tag);
_Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);
_Py_attribute_data_to_stat(&file_info, NULL, reparse_tag, NULL, NULL, &entry->win32_lstat);
Comment thread
thexai marked this conversation as resolved.
Resolved

/* ctime is only deprecated from 3.12, so we copy birthtime across */
entry->win32_lstat.st_ctime = entry->win32_lstat.st_birthtime;
Expand Down
50 changes: 35 additions & 15 deletions Python/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1103,15 +1103,23 @@ typedef union {


void
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, FILE_STANDARD_INFO* standard_info,
ULONG reparse_tag, FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
struct _Py_stat_struct *result)
{
memset(result, 0, sizeof(*result));
result->st_mode = attributes_to_mode(info->dwFileAttributes);
result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
result->st_dev = id_info ? id_info->VolumeSerialNumber : info->dwVolumeSerialNumber;
result->st_rdev = 0;

if (info) {
result->st_size = (((__int64)info->nFileSizeHigh) << 32) + info->nFileSizeLow;
result->st_nlink = info->nNumberOfLinks;
if (!id_info)
result->st_dev = info->dwVolumeSerialNumber;
}
if (standard_info && !info) {
result->st_size = standard_info->EndOfFile.QuadPart;
result->st_nlink = standard_info->NumberOfLinks;
}
Comment thread
thexai marked this conversation as resolved.
Resolved

/* st_ctime is deprecated, but we preserve the legacy value in our caller, not here */
if (basic_info) {
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
Expand All @@ -1123,9 +1131,9 @@ _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
}
result->st_nlink = info->nNumberOfLinks;

if (id_info) {
result->st_dev = id_info->VolumeSerialNumber;
id_128_to_ino file_id;
file_id.id = id_info->FileId;
result->st_ino = file_id.st_ino;
Expand All @@ -1134,19 +1142,31 @@ _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
if (!result->st_ino && !result->st_ino_high) {
/* should only occur for DirEntry_from_find_data, in which case the
index is likely to be zero anyway. */
result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
if (info)
result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
Comment thread
thexai marked this conversation as resolved.
Resolved
}

const DWORD fileAttributes = basic_info ? basic_info->FileAttributes : info->dwFileAttributes;

result->st_file_attributes = fileAttributes;
result->st_mode = attributes_to_mode(fileAttributes);
Comment thread
thexai marked this conversation as resolved.
Resolved

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

// For UWP compatibility since is not possible obtain the VolumeSerialNumber
// and FileId due security restriction and App isolation
#ifndef MS_WINDOWS_DESKTOP
result->st_dev = 1;
result->st_ino = basic_info->CreationTime.QuadPart;
Comment thread
thexai marked this conversation as resolved.
Resolved
#endif
}

void
Expand Down Expand Up @@ -1231,9 +1251,9 @@ int
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
{
#ifdef MS_WINDOWS
BY_HANDLE_FILE_INFORMATION info;
FILE_BASIC_INFO basicInfo;
FILE_ID_INFO idInfo;
FILE_STANDARD_INFO standardInfo = {0};
FILE_BASIC_INFO basicInfo = {0};
FILE_ID_INFO idInfo = {0};
FILE_ID_INFO *pIdInfo = &idInfo;
HANDLE h;
int type;
Expand Down Expand Up @@ -1266,7 +1286,7 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
return 0;
}

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

_Py_attribute_data_to_stat(&info, 0, &basicInfo, pIdInfo, status);
_Py_attribute_data_to_stat(NULL, &standardInfo, 0, &basicInfo, pIdInfo, status);
return 0;
#else
return fstat(fd, status);
Expand Down
Loading