diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index f99edc9eecc7..97397883e8e4 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -135,28 +135,28 @@ freebsd32_wait4(struct thread *td, struct freebsd32_wait4_args *uap) static void copy_statfs(struct statfs *in, struct statfs32 *out) { - + + statfs_scale_blocks(in, INT32_MAX); bzero(out, sizeof(*out)); CP(*in, *out, f_bsize); - CP(*in, *out, f_iosize); + out->f_iosize = MIN(in->f_iosize, INT32_MAX); CP(*in, *out, f_blocks); CP(*in, *out, f_bfree); CP(*in, *out, f_bavail); - CP(*in, *out, f_files); - CP(*in, *out, f_ffree); + out->f_files = MIN(in->f_files, INT32_MAX); + out->f_ffree = MIN(in->f_ffree, INT32_MAX); CP(*in, *out, f_fsid); CP(*in, *out, f_owner); CP(*in, *out, f_type); CP(*in, *out, f_flags); - CP(*in, *out, f_flags); - CP(*in, *out, f_syncwrites); - CP(*in, *out, f_asyncwrites); + out->f_syncwrites = MIN(in->f_syncwrites, INT32_MAX); + out->f_asyncwrites = MIN(in->f_asyncwrites, INT32_MAX); strlcpy(out->f_fstypename, in->f_fstypename, MFSNAMELEN); strlcpy(out->f_mntonname, in->f_mntonname, min(MNAMELEN, FREEBSD4_MNAMELEN)); - CP(*in, *out, f_syncreads); - CP(*in, *out, f_asyncreads); + out->f_syncreads = MIN(in->f_syncreads, INT32_MAX); + out->f_asyncreads = MIN(in->f_asyncreads, INT32_MAX); strlcpy(out->f_mntfromname, in->f_mntfromname, min(MNAMELEN, FREEBSD4_MNAMELEN)); } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 819a74c73a8d..d90e322b13cb 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -205,6 +205,47 @@ quotactl(td, uap) return (error); } +/* + * Used by statfs conversion routines to scale the block size up if + * necessary so that all of the block counts are <= 'max_size'. Note + * that 'max_size' should be a bitmask, i.e. 2^n - 1 for some non-zero + * value of 'n'. + */ +void +statfs_scale_blocks(struct statfs *sf, long max_size) +{ + uint64_t count; + int shift; + + KASSERT(powerof2(max_size + 1), ("%s: invalid max_size", __func__)); + + /* + * Attempt to scale the block counts to give a more accurate + * overview to userland of the ratio of free space to used + * space. To do this, find the largest block count and compute + * a divisor that lets it fit into a signed integer <= max_size. + */ + if (sf->f_bavail < 0) + count = -sf->f_bavail; + else + count = sf->f_bavail; + count = MAX(sf->f_blocks, MAX(sf->f_bfree, count)); + if (count <= max_size) + return; + + count >>= flsl(max_size); + shift = 0; + while (count > 0) { + shift++; + count >>=1; + } + + sf->f_bsize <<= shift; + sf->f_blocks >>= shift; + sf->f_bfree >>= shift; + sf->f_bavail >>= shift; +} + /* * Get filesystem statistics. */ @@ -636,12 +677,13 @@ cvtstatfs(nsp, osp) struct ostatfs *osp; { + statfs_scale_blocks(nsp, LONG_MAX); bzero(osp, sizeof(*osp)); - osp->f_bsize = MIN(nsp->f_bsize, LONG_MAX); + osp->f_bsize = nsp->f_bsize; osp->f_iosize = MIN(nsp->f_iosize, LONG_MAX); - osp->f_blocks = MIN(nsp->f_blocks, LONG_MAX); - osp->f_bfree = MIN(nsp->f_bfree, LONG_MAX); - osp->f_bavail = MIN(nsp->f_bavail, LONG_MAX); + osp->f_blocks = nsp->f_blocks; + osp->f_bfree = nsp->f_bfree; + osp->f_bavail = nsp->f_bavail; osp->f_files = MIN(nsp->f_files, LONG_MAX); osp->f_ffree = MIN(nsp->f_ffree, LONG_MAX); osp->f_owner = nsp->f_owner; diff --git a/sys/sys/mount.h b/sys/sys/mount.h index 6eeb5dd6be18..37dd9afeb4cf 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -653,6 +653,7 @@ struct mntarg *mount_arg(struct mntarg *ma, const char *name, const void *val, i struct mntarg *mount_argb(struct mntarg *ma, int flag, const char *name); struct mntarg *mount_argf(struct mntarg *ma, const char *name, const char *fmt, ...); struct mntarg *mount_argsu(struct mntarg *ma, const char *name, const void *val, int len); +void statfs_scale_blocks(struct statfs *sf, long max_size); struct vfsconf *vfs_byname(const char *); struct vfsconf *vfs_byname_kld(const char *, struct thread *td, int *); void vfs_mount_destroy(struct mount *);