Skip to content
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