Skip to content

Commit d530ab9

Browse files
committed
Only expose named members of struct statx in os.statx_result
1 parent de38430 commit d530ab9

File tree

2 files changed

+95
-115
lines changed

2 files changed

+95
-115
lines changed

Doc/library/os.rst

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3455,37 +3455,64 @@ features:
34553455

34563456
Mount ID.
34573457

3458+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3459+
userspace API headers >= 5.8.
3460+
34583461
.. attribute:: stx_dio_mem_align
34593462

34603463
Direct I/O memory buffer alignment requirement.
34613464

3465+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3466+
userspace API headers >= 6.1.
3467+
34623468
.. attribute:: stx_dio_offset_align
34633469

34643470
Direct I/O file offset alignment requirement.
34653471

3472+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3473+
userspace API headers >= 6.1.
3474+
34663475
.. attribute:: stx_subvol
34673476

34683477
Subvolume ID.
34693478

3479+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3480+
userspace API headers >= 6.10.
3481+
34703482
.. attribute:: stx_atomic_write_unit_min
34713483

34723484
Minimum size for direct I/O with torn-write protection.
34733485

3486+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3487+
userspace API headers >= 6.11.
3488+
34743489
.. attribute:: stx_atomic_write_unit_max
34753490

34763491
Maximum size for direct I/O with torn-write protection.
34773492

3493+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3494+
userspace API headers >= 6.11.
3495+
3496+
.. attribute:: stx_atomic_write_unit_max_opt
3497+
3498+
Maximum optimized size for direct I/O with torn-write protection.
3499+
3500+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3501+
userspace API headers >= 6.11.
3502+
34783503
.. attribute:: stx_atomic_write_segments_max
34793504

34803505
Maximum iovecs for direct I/O with torn-write protection.
34813506

3507+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3508+
userspace API headers >= 6.11.
3509+
34823510
.. attribute:: stx_dio_read_offset_align
34833511

34843512
Direct I/O file offset alignment requirement for reads.
34853513

3486-
.. attribute:: stx_atomic_write_unit_max_opt
3487-
3488-
Maximum optimized size for direct I/O with torn-write protection.
3514+
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
3515+
userspace API headers >= 6.14.
34893516

34903517
.. seealso:: The :manpage:`statx(2)` man page.
34913518

@@ -3514,7 +3541,9 @@ features:
35143541
STATX_WRITE_ATOMIC
35153542
STATX_DIO_READ_ALIGN
35163543

3517-
Bitflags for use as the *mask* parameter to :func:`os.statx`.
3544+
Bitflags for use in the *mask* parameter to :func:`os.statx`. Flags
3545+
including and after :const:`!STATX_MNT_ID` are only available when their
3546+
corresponding members in :class:`statx_result` are available.
35183547

35193548
.. availability:: Linux >= 4.11 with glibc >= 2.28.
35203549

Modules/posixmodule.c

Lines changed: 62 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -412,29 +412,26 @@ extern char *ctermid_r(char *);
412412
#ifdef HAVE_STATX
413413
/* until we can assume glibc 2.28 at runtime, we must weakly link */
414414
# pragma weak statx
415-
/* provide constants introduced later than statx itself */
416-
# ifndef STATX_MNT_ID
417-
# define STATX_MNT_ID 0x00001000U
418-
# endif
419-
# ifndef STATX_DIOALIGN
420-
# define STATX_DIOALIGN 0x00002000U
421-
# endif
422-
# ifndef STATX_MNT_ID_UNIQUE
423-
# define STATX_MNT_ID_UNIQUE 0x00004000U
424-
# endif
425-
# ifndef STATX_SUBVOL
426-
# define STATX_SUBVOL 0x00008000U
427-
# endif
428-
# ifndef STATX_WRITE_ATOMIC
429-
# define STATX_WRITE_ATOMIC 0x00010000U
430-
# endif
431-
# ifndef STATX_DIO_READ_ALIGN
432-
# define STATX_DIO_READ_ALIGN 0x00020000U
433-
# endif
434-
# define _Py_STATX_KNOWN (STATX_BASIC_STATS | STATX_BTIME | STATX_MNT_ID | \
435-
STATX_DIOALIGN | STATX_MNT_ID_UNIQUE | \
436-
STATX_SUBVOL | STATX_WRITE_ATOMIC | \
437-
STATX_DIO_READ_ALIGN)
415+
static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
416+
#ifdef STATX_MNT_ID
417+
| STATX_MNT_ID
418+
#endif
419+
#ifdef STATX_DIOALIGN
420+
| STATX_DIOALIGN
421+
#endif
422+
#ifdef STATX_MNT_ID_UNIQUE
423+
| STATX_MNT_ID_UNIQUE
424+
#endif
425+
#ifdef STATX_SUBVOL
426+
| STATX_SUBVOL
427+
#endif
428+
#ifdef STATX_WRITE_ATOMIC
429+
| STATX_WRITE_ATOMIC
430+
#endif
431+
#ifdef STATX_DIO_READ_ALIGN
432+
| STATX_DIO_READ_ALIGN
433+
#endif
434+
);
438435
#endif /* HAVE_STATX */
439436

440437

@@ -3319,14 +3316,11 @@ typedef struct {
33193316
PyObject_HEAD
33203317
double atime_sec, btime_sec, ctime_sec, mtime_sec;
33213318
dev_t rdev, dev;
3322-
/* Assertions in posixmodule_exec rely on struct statx being at the end. */
33233319
struct statx stx;
33243320
} Py_statx_result;
33253321

33263322
#define M(attr, type, offset, doc) \
33273323
{attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
3328-
#define MO(attr, type, offset, doc) \
3329-
M(#attr, type, offsetof(Py_statx_result, stx) + offset, doc)
33303324
#define MM(attr, type, member, doc) \
33313325
M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
33323326
#define MX(attr, type, member, doc) \
@@ -3355,78 +3349,64 @@ static PyMemberDef pystatx_result_members[] = {
33553349
MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"),
33563350
MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"),
33573351
MX(st_dev, Py_T_ULONGLONG, dev, "device"),
3358-
/* We may be building against old kernel API headers that do not have the
3359-
names of these members, so access them by offset. The reserved space in
3360-
struct statx was originally defined as arrays of u64, so later members
3361-
of other types must use getters to avoid a strict aliasing violation. */
3362-
MO(stx_mnt_id, Py_T_ULONGLONG, 144, "mount ID"),
3363-
MO(stx_subvol, Py_T_ULONGLONG, 160, "subvolume ID"),
3352+
#ifdef STATX_MNT_ID
3353+
MM(stx_mnt_id, Py_T_ULONGLONG, mnt_id, "mount ID"),
3354+
#endif
3355+
#ifdef STATX_DIOALIGN
3356+
MM(stx_dio_mem_align, Py_T_UINT, dio_mem_align,
3357+
"direct I/O memory buffer alignment"),
3358+
MM(stx_dio_offset_align, Py_T_UINT, dio_offset_align,
3359+
"direct I/O file offset alignment"),
3360+
#endif
3361+
#ifdef STATX_SUBVOL
3362+
MM(stx_subvol, Py_T_ULONGLONG, subvol, "subvolume ID"),
3363+
#endif
3364+
#ifdef STATX_WRITE_ATOMIC
3365+
MM(stx_atomic_write_unit_min, Py_T_UINT, atomic_write_unit_min,
3366+
"minimum size for direct I/O with torn-write protection"),
3367+
MM(stx_atomic_write_unit_max, Py_T_UINT, atomic_write_unit_max,
3368+
"maximum size for direct I/O with torn-write protection"),
3369+
MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
3370+
"maximum optimized size for direct I/O with torn-write protection"),
3371+
MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max,
3372+
"maximum iovecs for direct I/O with torn-write protection"),
3373+
#endif
3374+
#ifdef STATX_DIO_READ_ALIGN
3375+
MM(stx_dio_read_offset_align, Py_T_UINT, dio_read_offset_align,
3376+
"direct I/O file offset alignment for reads"),
3377+
#endif
33643378
{NULL},
33653379
};
33663380

33673381
#undef MX
33683382
#undef MM
3369-
#undef MO
33703383
#undef M
33713384

3372-
static PyObject *
3373-
pystatx_result_get_u32(PyObject *op, void *context) {
3374-
Py_statx_result *self = (Py_statx_result *) op;
3375-
uint16_t offset = (uintptr_t)context;
3376-
uint32_t val;
3377-
memcpy(&val, (void *)self + offset, sizeof(val));
3378-
return PyLong_FromUInt32(val);
3379-
}
3380-
33813385
static PyObject *
33823386
pystatx_result_get_nsec(PyObject *op, void *context)
33833387
{
3384-
Py_statx_result *self = (Py_statx_result *) op;
33853388
uint16_t offset = (uintptr_t)context;
3386-
struct statx_timestamp val;
3387-
memcpy(&val, (void *)self + offset, sizeof(val));
3389+
struct statx_timestamp *ts = (void*)op + offset;
33883390
_posixstate *state = PyType_GetModuleState(Py_TYPE(op));
33893391
assert(state != NULL);
3390-
return stat_nanosecond_timestamp(state, val.tv_sec, val.tv_nsec);
3392+
return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec);
33913393
}
33923394

33933395
/* The low 16 bits of the context pointer are the offset from the start of
33943396
Py_statx_result to the struct statx member. */
3395-
#define OFFSET_CONTEXT(offset) (void *)(offsetof(Py_statx_result, stx) + offset)
3396-
#define MEMBER_CONTEXT(name) OFFSET_CONTEXT(offsetof(struct statx, stx_##name))
3397-
3398-
#define G(attr, type, doc, context) \
3399-
{attr, pystatx_result_get_##type, NULL, PyDoc_STR(doc), context}
34003397
#define GM(attr, type, member, doc) \
3401-
G(#attr, type, doc, MEMBER_CONTEXT(member))
3402-
#define GO(attr, type, offset, doc) \
3403-
G(#attr, type, doc, OFFSET_CONTEXT(offset))
3398+
{#attr, pystatx_result_get_##type, NULL, PyDoc_STR(doc), \
3399+
(void *)(offsetof(Py_statx_result, stx.stx_##member))}
34043400

34053401
static PyGetSetDef pystatx_result_getset[] = {
34063402
GM(st_atime_ns, nsec, atime, "time of last access in nanoseconds"),
34073403
GM(st_birthtime_ns, nsec, btime, "time of creation in nanoseconds"),
34083404
GM(st_ctime_ns, nsec, ctime, "time of last change in nanoseconds"),
34093405
GM(st_mtime_ns, nsec, mtime, "time of last modification in nanoseconds"),
3410-
GO(stx_dio_mem_align, u32, 152, "direct I/O memory buffer alignment"),
3411-
GO(stx_dio_offset_align, u32, 156, "direct I/O file offset alignment"),
3412-
GO(stx_atomic_write_unit_min, u32, 168,
3413-
"minimum size for direct I/O with torn-write protection"),
3414-
GO(stx_atomic_write_unit_max, u32, 172,
3415-
"maximum size for direct I/O with torn-write protection"),
3416-
GO(stx_atomic_write_segments_max, u32, 176,
3417-
"maximum iovecs for direct I/O with torn-write protection"),
3418-
GO(stx_dio_read_offset_align, u32, 180,
3419-
"direct I/O file offset alignment for reads"),
3420-
GO(stx_atomic_write_unit_max_opt, u32, 184,
3421-
"maximum optimized size for direct I/O with torn-write protection"),
34223406
{NULL},
34233407
};
34243408

3425-
#undef GO
34263409
#undef GM
3427-
#undef G
3428-
#undef MEMBER_CONTEXT
3429-
#undef OFFSET_CONTEXT
34303410

34313411
static PyObject *
34323412
pystatx_result_repr(PyObject *op) {
@@ -18269,12 +18249,24 @@ all_ins(PyObject *m)
1826918249
if (PyModule_AddIntMacro(m, STATX_BLOCKS)) return -1;
1827018250
if (PyModule_AddIntMacro(m, STATX_BASIC_STATS)) return -1;
1827118251
if (PyModule_AddIntMacro(m, STATX_BTIME)) return -1;
18252+
#ifdef STATX_MNT_ID
1827218253
if (PyModule_AddIntMacro(m, STATX_MNT_ID)) return -1;
18254+
#endif
18255+
#ifdef STATX_DIOALIGN
1827318256
if (PyModule_AddIntMacro(m, STATX_DIOALIGN)) return -1;
18257+
#endif
18258+
#ifdef STATX_MNT_ID_UNIQUE
1827418259
if (PyModule_AddIntMacro(m, STATX_MNT_ID_UNIQUE)) return -1;
18260+
#endif
18261+
#ifdef STATX_SUBVOL
1827518262
if (PyModule_AddIntMacro(m, STATX_SUBVOL)) return -1;
18263+
#endif
18264+
#ifdef STATX_WRITE_ATOMIC
1827618265
if (PyModule_AddIntMacro(m, STATX_WRITE_ATOMIC)) return -1;
18266+
#endif
18267+
#ifdef STATX_DIO_READ_ALIGN
1827718268
if (PyModule_AddIntMacro(m, STATX_DIO_READ_ALIGN)) return -1;
18269+
#endif
1827818270
/* STATX_ALL intentionally omitted because it is deprecated */
1827918271
if (PyModule_AddIntMacro(m, AT_STATX_SYNC_AS_STAT)) return -1;
1828018272
if (PyModule_AddIntMacro(m, AT_STATX_FORCE_SYNC)) return -1;
@@ -18554,47 +18546,6 @@ posixmodule_exec(PyObject *m)
1855418546
#endif
1855518547

1855618548
#ifdef HAVE_STATX
18557-
#ifndef NDEBUG
18558-
/* struct statx may be extended in the future. Assert that our definition
18559-
of struct statx is large enough for all the members we expose to Python.
18560-
These asserts rely on struct statx being the last member of
18561-
Py_statx_result. If you hit these asserts, upgrade your kernel
18562-
userspace API and/or libc headers. */
18563-
for (const PyMemberDef *m = pystatx_result_members; m->name != NULL; ++m) {
18564-
Py_ssize_t size;
18565-
switch (m->type) {
18566-
case Py_T_USHORT:
18567-
size = 2;
18568-
break;
18569-
case Py_T_UINT:
18570-
size = 4;
18571-
break;
18572-
case Py_T_ULONGLONG:
18573-
case Py_T_DOUBLE:
18574-
size = 8;
18575-
break;
18576-
default:
18577-
assert(false);
18578-
}
18579-
assert(m->offset + size <= (Py_ssize_t)sizeof(Py_statx_result));
18580-
}
18581-
18582-
for (const PyGetSetDef *m = pystatx_result_getset; m->name != NULL; ++m) {
18583-
uint16_t offset = (uintptr_t)m->closure;
18584-
Py_ssize_t size;
18585-
if (m->get == pystatx_result_get_u32) {
18586-
size = 4;
18587-
}
18588-
else if (m->get == pystatx_result_get_nsec) {
18589-
size = sizeof(struct statx_timestamp);
18590-
}
18591-
else {
18592-
assert(false);
18593-
}
18594-
assert(offset + size <= (Py_ssize_t)sizeof(Py_statx_result));
18595-
}
18596-
#endif /* !NDEBUG */
18597-
1859818549
if (statx == NULL) {
1859918550
PyObject* dct = PyModule_GetDict(m);
1860018551
if (dct == NULL) {

0 commit comments

Comments
 (0)