#ifdef HAVE_CONFIG_H #include #endif #include #include "../internal/syscall.h" #if VLIBC_LEVEL_GE(2) /* * Linux struct statfs (kernel ABI, x86_64; linux/statfs.h). Layout and * sizes are LP64 facts: f_type..f_ffree are longs, f_fsid is an int pair, * then f_namelen/f_frsize/f_flags/f_spare[4] complete the 120 bytes the * kernel copies out (the same layout src/misc/pathconf.c pins for * pathconf()/fpathconf()). The signed kernel longs are non-negative on * every real filesystem, so they convert straight into the unsigned POSIX * counts below. */ struct vlibc_kstatfs { long f_type; long f_bsize; long f_blocks; long f_bfree; long f_bavail; long f_files; long f_ffree; int f_fsid[2]; long f_namelen; long f_frsize; long f_flags; long f_spare[4]; }; /* Map one kernel statfs record into the portable POSIX statvfs view. */ static void statvfs_fill(struct statvfs *out, const struct vlibc_kstatfs *in) { out->f_bsize = (unsigned long)in->f_bsize; out->f_frsize = in->f_frsize != 0 ? (unsigned long)in->f_frsize : (unsigned long)in->f_bsize; out->f_blocks = (unsigned long)in->f_blocks; out->f_bfree = (unsigned long)in->f_bfree; out->f_bavail = (unsigned long)in->f_bavail; out->f_files = (unsigned long)in->f_files; out->f_ffree = (unsigned long)in->f_ffree; out->f_favail = (unsigned long)in->f_ffree; /* Reconstruct the kernel's 64-bit fsid (val[0] holds the low word on * the little-endian x86_64; the same packing glibc's statvfs reports). */ out->f_fsid = ((unsigned long)(unsigned int)in->f_fsid[1] << 32) | (unsigned long)(unsigned int)in->f_fsid[0]; out->f_flag = (unsigned long)(in->f_flags & (ST_RDONLY | ST_NOSUID)); out->f_namemax = (unsigned long)in->f_namelen; } /* * statvfs()/fstatvfs(): filesystem statistics in the portable POSIX shape, * over SYS_statfs/SYS_fstatfs (which share the same kernel struct and the * same mapping). The kernel returns 0 on success after filling all 120 * bytes; failures come back as -errno and are translated by syscall_ret. */ int statvfs(const char *restrict path, struct statvfs *restrict buf) { struct vlibc_kstatfs fs; if (syscall_ret(__syscall2(SYS_statfs, (long)path, (long)&fs)) != 0) { return -1; } statvfs_fill(buf, &fs); return 0; } int fstatvfs(int fildes, struct statvfs *buf) { struct vlibc_kstatfs fs; if (syscall_ret(__syscall2(SYS_fstatfs, fildes, (long)&fs)) != 0) { return -1; } statvfs_fill(buf, &fs); return 0; } #endif /* VLIBC_LEVEL_GE(2) */