Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6e222a5

Browse files
GH-87804: Fix counter overflow in statvfs on macOS (#99570)
On macOS the statvfs interface returns block counts as 32-bit integers, and that results in bad reporting for larger disks. Therefore reimplement statvfs in terms of statfs, which does use 64-bit integers for block counts. Tested using a sparse filesystem image of 100TB.
1 parent e19103a commit 6e222a5

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
On macOS the result of ``os.statvfs`` and ``os.fstatvfs`` now correctly report the size of very large disks, in previous versions the reported number of blocks was wrong for disks with at least 2**32 blocks.

Modules/posixmodule.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@
5252
# define EX_OK EXIT_SUCCESS
5353
#endif
5454

55+
#ifdef __APPLE__
56+
/* Needed for the implementation of os.statvfs */
57+
# include <sys/param.h>
58+
# include <sys/mount.h>
59+
#endif
60+
5561
/* On android API level 21, 'AT_EACCESS' is not declared although
5662
* HAVE_FACCESSAT is defined. */
5763
#ifdef __ANDROID__
@@ -12886,6 +12892,59 @@ os_WSTOPSIG_impl(PyObject *module, int status)
1288612892
#endif
1288712893
#include <sys/statvfs.h>
1288812894

12895+
#ifdef __APPLE__
12896+
/* On macOS struct statvfs uses 32-bit integers for block counts,
12897+
* resulting in overflow when filesystems are larger tan 4TB. Therefore
12898+
* os.statvfs is implemented in terms of statfs(2).
12899+
*/
12900+
12901+
static PyObject*
12902+
_pystatvfs_fromstructstatfs(PyObject *module, struct statfs st) {
12903+
PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType;
12904+
PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType);
12905+
if (v == NULL)
12906+
return NULL;
12907+
12908+
long flags = 0;
12909+
if (st.f_flags & MNT_RDONLY) {
12910+
flags |= ST_RDONLY;
12911+
}
12912+
if (st.f_flags & MNT_NOSUID) {
12913+
flags |= ST_NOSUID;
12914+
}
12915+
12916+
_Static_assert(sizeof(st.f_blocks) == sizeof(long long), "assuming large file");
12917+
12918+
PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long) st.f_iosize));
12919+
PyStructSequence_SET_ITEM(v, 1, PyLong_FromLong((long) st.f_bsize));
12920+
PyStructSequence_SET_ITEM(v, 2,
12921+
PyLong_FromLongLong((long long) st.f_blocks));
12922+
PyStructSequence_SET_ITEM(v, 3,
12923+
PyLong_FromLongLong((long long) st.f_bfree));
12924+
PyStructSequence_SET_ITEM(v, 4,
12925+
PyLong_FromLongLong((long long) st.f_bavail));
12926+
PyStructSequence_SET_ITEM(v, 5,
12927+
PyLong_FromLongLong((long long) st.f_files));
12928+
PyStructSequence_SET_ITEM(v, 6,
12929+
PyLong_FromLongLong((long long) st.f_ffree));
12930+
PyStructSequence_SET_ITEM(v, 7,
12931+
PyLong_FromLongLong((long long) st.f_ffree));
12932+
PyStructSequence_SET_ITEM(v, 8, PyLong_FromLong((long) flags));
12933+
12934+
PyStructSequence_SET_ITEM(v, 9, PyLong_FromLong((long) NAME_MAX));
12935+
PyStructSequence_SET_ITEM(v, 10, PyLong_FromUnsignedLong(st.f_fsid.val[0]));
12936+
if (PyErr_Occurred()) {
12937+
Py_DECREF(v);
12938+
return NULL;
12939+
}
12940+
12941+
return v;
12942+
}
12943+
12944+
#else
12945+
12946+
12947+
1288912948
static PyObject*
1289012949
_pystatvfs_fromstructstatvfs(PyObject *module, struct statvfs st) {
1289112950
PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType;
@@ -12937,6 +12996,8 @@ _pystatvfs_fromstructstatvfs(PyObject *module, struct statvfs st) {
1293712996
return v;
1293812997
}
1293912998

12999+
#endif
13000+
1294013001

1294113002
/*[clinic input]
1294213003
os.fstatvfs
@@ -12954,6 +13015,22 @@ os_fstatvfs_impl(PyObject *module, int fd)
1295413015
{
1295513016
int result;
1295613017
int async_err = 0;
13018+
#ifdef __APPLE__
13019+
struct statfs st;
13020+
/* On macOS os.fstatvfs is implemented using fstatfs(2) because
13021+
* the former uses 32-bit values for block counts.
13022+
*/
13023+
do {
13024+
Py_BEGIN_ALLOW_THREADS
13025+
result = fstatfs(fd, &st);
13026+
Py_END_ALLOW_THREADS
13027+
} while (result != 0 && errno == EINTR &&
13028+
!(async_err = PyErr_CheckSignals()));
13029+
if (result != 0)
13030+
return (!async_err) ? posix_error() : NULL;
13031+
13032+
return _pystatvfs_fromstructstatfs(module, st);
13033+
#else
1295713034
struct statvfs st;
1295813035

1295913036
do {
@@ -12966,6 +13043,7 @@ os_fstatvfs_impl(PyObject *module, int fd)
1296613043
return (!async_err) ? posix_error() : NULL;
1296713044

1296813045
return _pystatvfs_fromstructstatvfs(module, st);
13046+
#endif
1296913047
}
1297013048
#endif /* defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H) */
1297113049

@@ -12989,6 +13067,28 @@ os_statvfs_impl(PyObject *module, path_t *path)
1298913067
/*[clinic end generated code: output=87106dd1beb8556e input=3f5c35791c669bd9]*/
1299013068
{
1299113069
int result;
13070+
13071+
#ifdef __APPLE__
13072+
/* On macOS os.statvfs is implemented using statfs(2)/fstatfs(2) because
13073+
* the former uses 32-bit values for block counts.
13074+
*/
13075+
struct statfs st;
13076+
13077+
Py_BEGIN_ALLOW_THREADS
13078+
if (path->fd != -1) {
13079+
result = fstatfs(path->fd, &st);
13080+
}
13081+
else
13082+
result = statfs(path->narrow, &st);
13083+
Py_END_ALLOW_THREADS
13084+
13085+
if (result) {
13086+
return path_error(path);
13087+
}
13088+
13089+
return _pystatvfs_fromstructstatfs(module, st);
13090+
13091+
#else
1299213092
struct statvfs st;
1299313093

1299413094
Py_BEGIN_ALLOW_THREADS
@@ -13006,6 +13106,7 @@ os_statvfs_impl(PyObject *module, path_t *path)
1300613106
}
1300713107

1300813108
return _pystatvfs_fromstructstatvfs(module, st);
13109+
#endif
1300913110
}
1301013111
#endif /* defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) */
1301113112

0 commit comments

Comments
 (0)