Skip to content

bpo-39533: Use statx on more recent Linux to expose st_flags and st_btime on all platforms #19125

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
Add statx-based stat implementation for Linux
  • Loading branch information
ntninja committed Sep 15, 2022
commit 54a23b5e1778ce427b0cec7bf190d11ebc7df75a
130 changes: 128 additions & 2 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,17 @@ extern char *ctermid_r(char *);
# define LSTAT win32_lstat
# define FSTAT _Py_fstat_noraise
# define STRUCT_STAT struct _Py_stat_struct
#elif defined(HAVE_LINUX_STATX)
# define STAT linux_stat
# define LSTAT linux_lstat
# define FSTAT linux_fstat
# define FSTATAT linux_fstatat
# define STRUCT_STAT struct statx
#else
# define STAT stat
# define LSTAT lstat
# define FSTAT fstat
# define FSTATAT fstatat
# define STRUCT_STAT struct stat
#endif

Expand Down Expand Up @@ -2066,6 +2073,39 @@ win32_stat(const wchar_t* path, struct _Py_stat_struct *result)

#endif /* MS_WINDOWS */


#ifdef HAVE_LINUX_STATX

// Extend this list when adding support for new Linux statx fields
#define LINUX_STATX_MASK STATX_BASIC_STATS | STATX_BTIME

static int
linux_stat(const char* path, struct statx* result)
{
return statx(AT_FDCWD, path, 0, LINUX_STATX_MASK, result);
}

static int
linux_lstat(const char* path, struct statx* result)
{
return statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, LINUX_STATX_MASK, result);
}

static int
linux_fstat(int fd, struct statx* result)
{
return statx(fd, "", AT_EMPTY_PATH, LINUX_STATX_MASK, result);
}

static int
linux_fstatat(int dirfd, const char* path, struct statx* result, int flags)
{
return statx(dirfd, path, flags, LINUX_STATX_MASK, result);
}

#endif /* HAVE_LINUX_STATX */


PyDoc_STRVAR(stat_result__doc__,
"stat_result: Result from stat, fstat, or lstat.\n\n\
This object may be accessed either as a tuple of\n\
Expand Down Expand Up @@ -2121,6 +2161,10 @@ 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 supported file attribute bits on this filesystem"},
#endif
{0}
};
Expand Down Expand Up @@ -2173,6 +2217,14 @@ 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_REPARSE_TAG_IDX+2)
#else
#define ST_ATTRIBUTES_IDX ST_REPARSE_TAG_IDX
#define ST_ATTRIBUTES_MASK_IDX ST_REPARSE_TAG_IDX
#endif

static PyStructSequence_Desc stat_result_desc = {
"stat_result", /* name */
stat_result__doc__, /* doc */
Expand Down Expand Up @@ -2357,6 +2409,78 @@ fill_time(PyObject *module, PyObject *v, int index1, int index2, int index3,
Py_XDECREF(float_s);
}

#ifdef HAVE_LINUX_STATX

/* pack a system statx C structure into the Python stat tuple
(used by posix_stat() and posix_fstat()) */
static PyObject*
_pystat_fromstructstat(PyObject *module, struct statx* stx)
{
unsigned long long attributes;
PyObject *StatResultType = get_posix_state(module)->StatResultType;
PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType);
if (v == NULL)
return NULL;

if(stx->stx_mask & STATX_TYPE) {
PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)stx->stx_mode));
}
if(stx->stx_mask & STATX_INO) {
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(stx->stx_ino));
PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(stx->stx_ino));
}
PyStructSequence_SET_ITEM(v, 2, _PyLong_FromDev(makedev(stx->stx_dev_major, stx->stx_dev_minor)));
if(stx->stx_mask & STATX_NLINK) {
PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long)stx->stx_nlink));
}
if(stx->stx_mask & STATX_UID) {
PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(stx->stx_uid));
}
if(stx->stx_mask & STATX_GID) {
PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(stx->stx_gid));
}
Py_BUILD_ASSERT(sizeof(long long) >= sizeof(stx->stx_size));
PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(stx->stx_size));

if(stx->stx_mask & STATX_ATIME) {
fill_time(module, v, 7, 10, 14, stx->stx_atime.tv_sec, stx->stx_atime.tv_nsec);
}
if(stx->stx_mask & STATX_MTIME) {
fill_time(module, v, 8, 11, 15, stx->stx_mtime.tv_sec, stx->stx_mtime.tv_nsec);
}
if(stx->stx_mask & STATX_CTIME) {
fill_time(module, v, 9, 12, 16, stx->stx_ctime.tv_sec, stx->stx_ctime.tv_nsec);
}
if(stx->stx_mask & STATX_BTIME) {
fill_time(module, v, -1, 13, 17, stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
}

attributes = stx->stx_attributes & stx->stx_attributes_mask;
PyStructSequence_SET_ITEM(v, ST_ATTRIBUTES_IDX,
PyLong_FromUnsignedLongLong(attributes));
PyStructSequence_SET_ITEM(v, ST_ATTRIBUTES_MASK_IDX,
PyLong_FromUnsignedLongLong(stx->stx_attributes_mask));

PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
PyLong_FromLong((long)stx->stx_blksize));
if(stx->stx_mask & STATX_BLOCKS) {
PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX,
PyLong_FromLong((long)stx->stx_blocks));
}

PyStructSequence_SET_ITEM(v, ST_RDEV_IDX,
_PyLong_FromDev(makedev(stx->stx_rdev_major, stx->stx_rdev_minor)));

if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}

return v;
}

#else /* HAVE_LINUX_STATX */

/* pack a system stat C structure into the Python stat tuple
(used by posix_stat() and posix_fstat()) */
static PyObject*
Expand Down Expand Up @@ -2473,6 +2597,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
return v;
}

#endif /* !HAVE_LINUX_STATX */

/* POSIX methods */


Expand Down Expand Up @@ -2515,7 +2641,7 @@ posix_do_stat(PyObject *module, const char *function_name, path_t *path,
#ifdef HAVE_FSTATAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
if (HAVE_FSTATAT_RUNTIME) {
result = fstatat(dir_fd, path->narrow, &st,
result = FSTATAT(dir_fd, path->narrow, &st,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);

} else {
Expand Down Expand Up @@ -13560,7 +13686,7 @@ DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks)
#ifdef HAVE_FSTATAT
if (HAVE_FSTATAT_RUNTIME) {
Py_BEGIN_ALLOW_THREADS
result = fstatat(self->dir_fd, path, &st,
result = FSTATAT(self->dir_fd, path, &st,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
Py_END_ALLOW_THREADS
} else
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy