Skip to content

Commit

Permalink
pythongh-99726: Improves correctness of stat results for Windows, and…
Browse files Browse the repository at this point in the history
… uses faster API when available
  • Loading branch information
zooba committed Feb 22, 2023
1 parent d5c7954 commit 5187810
Show file tree
Hide file tree
Showing 10 changed files with 329 additions and 41 deletions.
39 changes: 28 additions & 11 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2790,6 +2790,10 @@ features:
Added support for the :class:`~os.PathLike` interface. Added support
for :class:`bytes` paths on Windows.

.. versionchanged:: 3.12
The ``st_ctime`` attributes of a stat result are now set to zero, rather
than being incorrectly set to the creation time of the file.


.. function:: stat(path, *, dir_fd=None, follow_symlinks=True)

Expand Down Expand Up @@ -2905,10 +2909,7 @@ features:

.. attribute:: st_ctime

Platform dependent:

* the time of most recent metadata change on Unix,
* the time of creation on Windows, expressed in seconds.
Time of most recent metadata change expressed in seconds.

.. attribute:: st_atime_ns

Expand All @@ -2921,11 +2922,8 @@ features:

.. attribute:: st_ctime_ns

Platform dependent:

* the time of most recent metadata change on Unix,
* the time of creation on Windows, expressed in nanoseconds as an
integer.
Time of most recent metadata change expressed in nanoseconds as an
integer.

.. note::

Expand Down Expand Up @@ -2975,7 +2973,7 @@ features:

.. attribute:: st_birthtime

Time of file creation.
Time of file creation. This is also available on Windows.

On Solaris and derivatives, the following attributes may also be
available:
Expand All @@ -2999,7 +2997,8 @@ features:

File type.

On Windows systems, the following attributes are also available:
On Windows systems, as well as ``st_birthtime`` above, the following
attributes are also available:

.. attribute:: st_file_attributes

Expand Down Expand Up @@ -3049,6 +3048,24 @@ features:
files as :const:`S_IFCHR`, :const:`S_IFIFO` or :const:`S_IFBLK`
as appropriate.

.. versionchanged:: 3.12
On Windows, :attr:`st_ctime` now also contains the last metadata
change time, for consistency with other platforms.
Use :attr:`st_birthtime` for the creation time when available.

.. versionchanged:: 3.12
On Windows, :attr:`st_ino` may now be up to 128 bits, depending
on the file system. Previously it would not be above 64 bits, and
larger file identifiers would be arbitrarily packed.

.. versionchanged:: 3.12
On Windows, :attr:`st_rdev` no longer returns a value. Previously
it would contain the same as :attr:`st_dev`, which was incorrect.

.. versionadded:: 3.12
Added the :attr:`st_birthtime` on Windows


.. function:: statvfs(path)

Perform a :c:func:`statvfs` system call on the given path. The return value is
Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,15 @@ os
method to check if the entry is a junction.
(Contributed by Charles Machalow in :gh:`99547`.)

* :func:`os.stat` and :func:`os.lstat` are now more accurate on Windows.
The ``st_birthtime`` field will now be filled with the creation time
of the file, and ``st_ctime`` now returns the last metadata change,
for consistency with other platforms. ``st_ino`` may now be up to 128
bits, depending on your file system, and ``st_rdev`` is always set to
zero rather than to incorrect values.
Both functions may be significantly faster on newer releases of
Windows. (Contributed by Steve Dower in :gh:`99726`.)

os.path
-------

Expand Down
5 changes: 4 additions & 1 deletion Include/internal/pycore_fileutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ PyAPI_FUNC(PyObject *) _Py_device_encoding(int);

#ifdef MS_WINDOWS
struct _Py_stat_struct {
unsigned long st_dev;
uint64_t st_dev;
uint64_t st_ino;
unsigned short st_mode;
int st_nlink;
Expand All @@ -80,8 +80,11 @@ struct _Py_stat_struct {
int st_mtime_nsec;
time_t st_ctime;
int st_ctime_nsec;
time_t st_birthtime;
int st_birthtime_nsec;
unsigned long st_file_attributes;
unsigned long st_reparse_tag;
uint64_t st_ino_high;
};
#else
# define _Py_stat_struct stat
Expand Down
80 changes: 80 additions & 0 deletions Include/internal/pycore_fileutils_windows.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#ifndef Py_INTERNAL_FILEUTILS_WINDOWS_H
#define Py_INTERNAL_FILEUTILS_WINDOWS_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "Py_BUILD_CORE must be defined to include this header"
#endif

#ifdef MS_WINDOWS

#if !defined(NTDDI_WIN10_NI) || !(NTDDI_VERSION >= NTDDI_WIN10_NI)
typedef struct _FILE_STAT_BASIC_INFORMATION {
LARGE_INTEGER FileId;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER AllocationSize;
LARGE_INTEGER EndOfFile;
ULONG FileAttributes;
ULONG ReparseTag;
ULONG NumberOfLinks;
ULONG DeviceType;
ULONG DeviceCharacteristics;
ULONG Reserved;
ULONGLONG FileIdHigh;
ULONGLONG VolumeSerialNumber;
} FILE_STAT_BASIC_INFORMATION;

typedef enum _FILE_INFO_BY_NAME_CLASS {
FileStatByNameInfo,
FileStatLxByNameInfo,
FileCaseSensitiveByNameInfo,
FileStatBasicByNameInfo,
MaximumFileInfoByNameClass
} FILE_INFO_BY_NAME_CLASS;
#endif

typedef BOOL (WINAPI *PGetFileInformationByName)(
PCWSTR FileName,
FILE_INFO_BY_NAME_CLASS FileInformationClass,
PVOID FileInfoBuffer,
ULONG FileInfoBufferSize
);

static inline BOOL _Py_GetFileInformationByName(
PCWSTR FileName,
FILE_INFO_BY_NAME_CLASS FileInformationClass,
PVOID FileInfoBuffer,
ULONG FileInfoBufferSize
) {
static PGetFileInformationByName GetFileInformationByName = NULL;
static int GetFileInformationByName_init = -1;

if (GetFileInformationByName_init < 0) {
HMODULE hMod = LoadLibraryW(L"api-ms-win-core-file-l2-1-4");
GetFileInformationByName_init = 0;
if (hMod) {
GetFileInformationByName = (PGetFileInformationByName)GetProcAddress(
hMod, "GetFileInformationByName");
if (GetFileInformationByName) {
GetFileInformationByName_init = 1;
} else {
FreeLibrary(hMod);
}
}
}

if (GetFileInformationByName_init <= 0) {
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
return GetFileInformationByName(FileName, FileInformationClass, FileInfoBuffer, FileInfoBufferSize);
}

#endif

#endif
3 changes: 2 additions & 1 deletion Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -4164,7 +4164,8 @@ def assert_stat_equal(self, stat1, stat2, skip_fields):
for attr in dir(stat1):
if not attr.startswith("st_"):
continue
if attr in ("st_dev", "st_ino", "st_nlink"):
if attr in ("st_dev", "st_ino", "st_nlink", "st_ctime",
"st_ctime_ns"):
continue
self.assertEqual(getattr(stat1, attr),
getattr(stat2, attr),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improves correctness of stat results for Windows, and uses faster API when
available
Loading

0 comments on commit 5187810

Please sign in to comment.