-
-
Notifications
You must be signed in to change notification settings - Fork 33k
gh-83714: Use statx on Linux 4.11 and later in os.stat #136334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
99c9efd
bpo-39533: Use statx on Linux 4.11 and later in os.stat
jbosboom cebd5b7
Fix versionadded documentation syntax
jbosboom ffd7a89
Add blurb
jbosboom d53650a
Fix typo stx_subvol -> st_subvol in docs
jbosboom 4e4e917
Allow statx to be explicitly configured out for old glibc
jbosboom bc3155b
Implement os.statx
jbosboom b0e8276
Use correct function name in os.statx argument validation errors
jbosboom 1a8a6d6
Merge branch 'main' into statx
jbosboom f71315e
Move statx-specific members from os.stat_result to new os.statx_result
jbosboom 1f1e959
make regen-configure
jbosboom 7440397
Reimplement os.statx_result using PyMemberDef and PyGetSetDef; remove…
jbosboom 534e33f
Remove statx-specific members from os.stat_result documentation
jbosboom 7dc75d0
Simplify statx configure check
jbosboom 894efb5
Document os.statx
jbosboom d5bc601
Prepare for review
jbosboom 5b36cf0
Document st_birthtime fallback behavior
jbosboom 6be85a3
Implement os.stat with statx only if the statx syscall works
jbosboom c32ee72
Test statx PathLike and dir_fd handling
jbosboom c84f783
statx docs fixes
jbosboom File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -408,6 +408,18 @@ extern char *ctermid_r(char *); | |
# define STRUCT_STAT struct stat | ||
#endif | ||
|
||
#if defined(__linux__) && defined(STATX_BASIC_STATS) | ||
# pragma weak statx | ||
# define HAVE_LINUX_STATX 1 | ||
# define HAVE_LINUX_STATX_RUNTIME (statx != NULL) | ||
/* provide definitions introduced later than statx itself */ | ||
# define _Py_STATX_MNT_ID 0x00001000U | ||
# define _Py_STATX_SUBVOL 0x00008000U | ||
# define _Py_STATX_MASK (STATX_BASIC_STATS | STATX_BTIME | _Py_STATX_MNT_ID | _Py_STATX_SUBVOL) | ||
# define _Py_STATX_MNT_ID_OFFSET 144 | ||
# define _Py_STATX_SUBVOL_OFFSET 160 | ||
#endif | ||
|
||
|
||
#if !defined(EX_OK) && defined(EXIT_SUCCESS) | ||
# define EX_OK EXIT_SUCCESS | ||
|
@@ -2353,10 +2365,10 @@ static PyStructSequence_Field stat_result_fields[] = { | |
#ifdef HAVE_STRUCT_STAT_ST_GEN | ||
{"st_gen", "generation number"}, | ||
#endif | ||
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) | ||
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(HAVE_LINUX_STATX) || defined(MS_WINDOWS) | ||
{"st_birthtime", "time of creation"}, | ||
#endif | ||
#ifdef MS_WINDOWS | ||
#if defined(HAVE_LINUX_STATX) || defined(MS_WINDOWS) | ||
{"st_birthtime_ns", "time of creation in nanoseconds"}, | ||
#endif | ||
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES | ||
|
@@ -2367,6 +2379,12 @@ static PyStructSequence_Field stat_result_fields[] = { | |
#endif | ||
#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG | ||
{"st_reparse_tag", "Windows reparse tag"}, | ||
#endif | ||
#ifdef HAVE_LINUX_STATX | ||
{"st_attributes", "Linux file attribute bits"}, | ||
{"st_attributes_mask", "Linux file attribute bits supported by this file"}, | ||
{"st_mnt_id", "Linux mount ID for /proc/self/mountinfo"}, | ||
{"st_subvol", "Linux subvolume identifier"}, | ||
#endif | ||
{0} | ||
}; | ||
|
@@ -2401,13 +2419,13 @@ static PyStructSequence_Field stat_result_fields[] = { | |
#define ST_GEN_IDX ST_FLAGS_IDX | ||
#endif | ||
|
||
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) | ||
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(HAVE_LINUX_STATX) || defined(MS_WINDOWS) | ||
#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) | ||
#else | ||
#define ST_BIRTHTIME_IDX ST_GEN_IDX | ||
#endif | ||
|
||
#ifdef MS_WINDOWS | ||
#if defined(HAVE_LINUX_STATX) || defined(MS_WINDOWS) | ||
#define ST_BIRTHTIME_NS_IDX (ST_BIRTHTIME_IDX+1) | ||
#else | ||
#define ST_BIRTHTIME_NS_IDX ST_BIRTHTIME_IDX | ||
|
@@ -2431,6 +2449,13 @@ static PyStructSequence_Field stat_result_fields[] = { | |
#define ST_REPARSE_TAG_IDX ST_FSTYPE_IDX | ||
#endif | ||
|
||
#ifdef HAVE_LINUX_STATX | ||
#define ST_ATTRIBUTES_IDX (ST_REPARSE_TAG_IDX+1) | ||
#define ST_ATTRIBUTES_MASK_IDX (ST_ATTRIBUTES_IDX+1) | ||
#define ST_MNT_ID_IDX (ST_ATTRIBUTES_MASK_IDX+1) | ||
#define ST_SUBVOL_IDX (ST_MNT_ID_IDX+1) | ||
#endif | ||
|
||
static PyStructSequence_Desc stat_result_desc = { | ||
"stat_result", /* name */ | ||
stat_result__doc__, /* doc */ | ||
|
@@ -2790,6 +2815,99 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) | |
#undef SET_ITEM | ||
} | ||
|
||
#ifdef HAVE_LINUX_STATX | ||
static PyObject* | ||
_pystat_fromstructstatx(PyObject *module, struct statx *st) | ||
{ | ||
assert(!PyErr_Occurred()); | ||
|
||
PyObject *StatResultType = get_posix_state(module)->StatResultType; | ||
PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType); | ||
if (v == NULL) { | ||
return NULL; | ||
} | ||
|
||
#define SET_ITEM(pos, expr) \ | ||
do { \ | ||
PyObject *obj = (expr); \ | ||
if (obj == NULL) { \ | ||
goto error; \ | ||
} \ | ||
PyStructSequence_SET_ITEM(v, (pos), obj); \ | ||
} while (0) | ||
|
||
SET_ITEM(0, PyLong_FromLong((long)st->stx_mode)); | ||
static_assert(sizeof(unsigned long long) >= sizeof(st->stx_ino), | ||
"statx.stx_ino is larger than unsigned long long"); | ||
SET_ITEM(1, PyLong_FromUnsignedLongLong(st->stx_ino)); | ||
dev_t dev = makedev(st->stx_dev_major, st->stx_dev_minor); | ||
SET_ITEM(2, _PyLong_FromDev(dev)); | ||
|
||
SET_ITEM(3, PyLong_FromLong((long)st->stx_nlink)); | ||
SET_ITEM(4, _PyLong_FromUid(st->stx_uid)); | ||
SET_ITEM(5, _PyLong_FromGid(st->stx_gid)); | ||
static_assert(sizeof(long long) >= sizeof(st->stx_size), | ||
"statx.stx_size is larger than long long"); | ||
SET_ITEM(6, PyLong_FromLongLong(st->stx_size)); | ||
|
||
if (fill_time(module, v, 7, 10, 13, st->stx_atime.tv_sec, | ||
st->stx_atime.tv_nsec) < 0) { | ||
goto error; | ||
} | ||
if (fill_time(module, v, 8, 11, 14, st->stx_mtime.tv_sec, | ||
st->stx_mtime.tv_nsec) < 0) { | ||
goto error; | ||
} | ||
if (fill_time(module, v, 9, 12, 15, st->stx_ctime.tv_sec, | ||
st->stx_ctime.tv_nsec) < 0) { | ||
goto error; | ||
} | ||
if (st->stx_mask & STATX_BTIME) { | ||
if (fill_time(module, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX, | ||
st->stx_btime.tv_sec, st->stx_btime.tv_nsec) < 0) { | ||
goto error; | ||
} | ||
} | ||
else { | ||
SET_ITEM(ST_BIRTHTIME_IDX, PyFloat_FromDouble(0.0)); | ||
SET_ITEM(ST_BIRTHTIME_NS_IDX, _PyLong_GetZero()); | ||
} | ||
|
||
SET_ITEM(ST_BLKSIZE_IDX, PyLong_FromLong((long)st->stx_blksize)); | ||
SET_ITEM(ST_BLOCKS_IDX, PyLong_FromLong((long)st->stx_blocks)); | ||
dev_t rdev = makedev(st->stx_rdev_major, st->stx_rdev_minor); | ||
SET_ITEM(ST_RDEV_IDX, _PyLong_FromDev(rdev)); | ||
|
||
SET_ITEM(ST_ATTRIBUTES_IDX, PyLong_FromUnsignedLong(st->stx_attributes)); | ||
SET_ITEM(ST_ATTRIBUTES_MASK_IDX, PyLong_FromLong(st->stx_attributes_mask)); | ||
|
||
if (st->stx_mask & _Py_STATX_MNT_ID) { | ||
void* stx_mnt_id = ((unsigned char*)st) + _Py_STATX_MNT_ID_OFFSET; | ||
SET_ITEM(ST_MNT_ID_IDX, PyLong_FromUnsignedNativeBytes(stx_mnt_id, 8, -1)); | ||
} | ||
else { | ||
SET_ITEM(ST_MNT_ID_IDX, Py_None); | ||
} | ||
|
||
if (st->stx_mask & _Py_STATX_SUBVOL) { | ||
void* stx_subvol = ((unsigned char*)st) + _Py_STATX_SUBVOL_OFFSET; | ||
SET_ITEM(ST_SUBVOL_IDX, PyLong_FromUnsignedNativeBytes(stx_subvol, 8, -1)); | ||
} | ||
else { | ||
SET_ITEM(ST_SUBVOL_IDX, Py_None); | ||
} | ||
|
||
assert(!PyErr_Occurred()); | ||
return v; | ||
|
||
error: | ||
Py_DECREF(v); | ||
return NULL; | ||
|
||
#undef SET_ITEM | ||
} | ||
#endif /* HAVE_LINUX_STATX */ | ||
|
||
/* POSIX methods */ | ||
|
||
|
||
|
@@ -2814,6 +2932,36 @@ posix_do_stat(PyObject *module, const char *function_name, path_t *path, | |
fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks)) | ||
return NULL; | ||
|
||
#ifdef HAVE_LINUX_STATX | ||
struct statx stx = {}; | ||
static int statx_works = -1; | ||
if (HAVE_LINUX_STATX_RUNTIME && statx_works != 0) { | ||
int flags = AT_NO_AUTOMOUNT; | ||
flags |= follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; | ||
Py_BEGIN_ALLOW_THREADS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I don't see a reason not to start allowing threads a bit earlier; aren't touching the interpreter in the lines just before this |
||
if (path->fd != -1) { | ||
flags |= AT_EMPTY_PATH; | ||
result = statx(path->fd, "", flags, _Py_STATX_MASK, &stx); | ||
} | ||
else { | ||
result = statx(dir_fd, path->narrow, flags, _Py_STATX_MASK, &stx); | ||
} | ||
Py_END_ALLOW_THREADS | ||
|
||
if (result == -1) { | ||
if (statx_works == -1) { | ||
statx_works = (errno != ENOSYS); | ||
} | ||
if (statx_works) { | ||
return path_error(path); | ||
} | ||
} | ||
else { | ||
return _pystat_fromstructstatx(module, &stx); | ||
} | ||
} | ||
#endif /* HAVE_LINUX_STATX */ | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
if (path->fd != -1) | ||
result = FSTAT(path->fd, &st); | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.