@@ -2076,8 +2076,14 @@ win32_stat(const wchar_t* path, struct _Py_stat_struct *result)
20762076
20772077#ifdef HAVE_LINUX_STATX
20782078
2079+ #ifdef STATX_MNT_ID // Added in Linux 5.8
2080+ # define _PY_STATX_MNT_ID STATX_MNT_ID
2081+ #else
2082+ # define _PY_STATX_MNT_ID 0x00001000U
2083+ #endif
2084+
20792085// Extend this list when adding support for new Linux statx fields
2080- #define LINUX_STATX_MASK STATX_BASIC_STATS | STATX_BTIME
2086+ #define LINUX_STATX_MASK STATX_BASIC_STATS | STATX_BTIME | _PY_STATX_MNT_ID
20812087
20822088static int
20832089linux_stat (const char * path , struct statx * result )
@@ -2165,6 +2171,7 @@ static PyStructSequence_Field stat_result_fields[] = {
21652171#ifdef HAVE_LINUX_STATX
21662172 {"st_attributes" , "Linux file attribute bits" },
21672173 {"st_attributes_mask" , "Linux supported file attribute bits on this filesystem" },
2174+ {"st_mnt_id" , "Linux mount ID of the mount containing the file" },
21682175#endif
21692176 {0 }
21702177};
@@ -2220,9 +2227,11 @@ static PyStructSequence_Field stat_result_fields[] = {
22202227#ifdef HAVE_LINUX_STATX
22212228#define ST_ATTRIBUTES_IDX (ST_REPARSE_TAG_IDX+1)
22222229#define ST_ATTRIBUTES_MASK_IDX (ST_REPARSE_TAG_IDX+2)
2230+ #define ST_MNT_ID_IDX (ST_REPARSE_TAG_IDX+3)
22232231#else
22242232#define ST_ATTRIBUTES_IDX ST_REPARSE_TAG_IDX
22252233#define ST_ATTRIBUTES_MASK_IDX ST_REPARSE_TAG_IDX
2234+ #define ST_MNT_ID_IDX ST_REPARSE_TAG_IDX
22262235#endif
22272236
22282237static PyStructSequence_Desc stat_result_desc = {
@@ -2464,8 +2473,10 @@ _pystat_fromstructstat(PyObject *module, struct statx* stx)
24642473
24652474 // Map statx flags to BSD flags
24662475 //
2467- // The contants used here are not defined on Linux, are available to
2468- // Python users through the "stat" module.
2476+ // The constants used here are not defined on Linux but are available to
2477+ // Python users through the "stat" module. In general, try to follow
2478+ // FreeBSD semantics when adding a mapping here and refrain from mapping
2479+ // attributes that don't have an obvious equivalent.
24692480 flags = 0 ;
24702481 if (attributes & STATX_ATTR_COMPRESSED ) {
24712482 flags |= 0x00000020 ; // UF_COMPRESSED
@@ -2482,6 +2493,17 @@ _pystat_fromstructstat(PyObject *module, struct statx* stx)
24822493 if (attributes & STATX_ATTR_ENCRYPTED ) {
24832494 flags |= 0x00002000 ; // UF_ENCRYPTED
24842495 }
2496+ // Note: There is nothing in the FreeBSD disk flags list resembling
2497+ // `STATX_ATTR_VERITY` or `STATX_ATTR_DAX`, so leave these unmapped.
2498+ //
2499+ // In addition (as of Linux 5.19), `STATX_ATTR_DAX` does not ever appear
2500+ // to be reported when querying on-disk files that previously had the
2501+ // corresponding "x" flag set using `chattr(1)`, so even if FreeBSD adds
2502+ // a corresponding flag mapping it would likely be of little use.
2503+ //
2504+ // If these flags ever need to be mapped, remember to add a compatibility
2505+ // define for them next to the definition of `LINUX_STATX_MASK`, to ensure
2506+ // builds will continue working on older C library versions.
24852507 PyStructSequence_SET_ITEM (v , 18 , PyLong_FromLong (flags ));
24862508
24872509 PyStructSequence_SET_ITEM (v , ST_BLKSIZE_IDX ,
@@ -2494,6 +2516,23 @@ _pystat_fromstructstat(PyObject *module, struct statx* stx)
24942516 PyStructSequence_SET_ITEM (v , ST_RDEV_IDX ,
24952517 _PyLong_FromDev (makedev (stx -> stx_rdev_major , stx -> stx_rdev_minor )));
24962518
2519+ if (stx -> stx_mask & _PY_STATX_MNT_ID ) {
2520+ PyStructSequence_SET_ITEM (v , ST_MNT_ID_IDX , PyLong_FromUnsignedLongLong (
2521+ #ifdef STATX_MNT_ID
2522+ stx -> stx_mnt_id
2523+ #else
2524+ // `stx_mnt_id` is the next 64-bit field following `stx_dev_minor`
2525+ //
2526+ // It is safe to assume its going to be there even if the C library
2527+ // does not support it yet, since the size of `struct statx` is
2528+ // constant and value presence is only indicated by the kernel in
2529+ // the `stx_mask` field queried above if the field is actually
2530+ // supported and was set.
2531+ * ((unsigned long long * ) ((& stx -> stx_dev_minor ) + 1 ))
2532+ #endif /* STATX_MNT_ID */
2533+ ));
2534+ }
2535+
24972536 if (PyErr_Occurred ()) {
24982537 Py_DECREF (v );
24992538 return NULL ;
0 commit comments