cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

statfs.c (9962B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/syscalls.h>
      3#include <linux/export.h>
      4#include <linux/fs.h>
      5#include <linux/file.h>
      6#include <linux/mount.h>
      7#include <linux/namei.h>
      8#include <linux/statfs.h>
      9#include <linux/security.h>
     10#include <linux/uaccess.h>
     11#include <linux/compat.h>
     12#include "internal.h"
     13
     14static int flags_by_mnt(int mnt_flags)
     15{
     16	int flags = 0;
     17
     18	if (mnt_flags & MNT_READONLY)
     19		flags |= ST_RDONLY;
     20	if (mnt_flags & MNT_NOSUID)
     21		flags |= ST_NOSUID;
     22	if (mnt_flags & MNT_NODEV)
     23		flags |= ST_NODEV;
     24	if (mnt_flags & MNT_NOEXEC)
     25		flags |= ST_NOEXEC;
     26	if (mnt_flags & MNT_NOATIME)
     27		flags |= ST_NOATIME;
     28	if (mnt_flags & MNT_NODIRATIME)
     29		flags |= ST_NODIRATIME;
     30	if (mnt_flags & MNT_RELATIME)
     31		flags |= ST_RELATIME;
     32	if (mnt_flags & MNT_NOSYMFOLLOW)
     33		flags |= ST_NOSYMFOLLOW;
     34	return flags;
     35}
     36
     37static int flags_by_sb(int s_flags)
     38{
     39	int flags = 0;
     40	if (s_flags & SB_SYNCHRONOUS)
     41		flags |= ST_SYNCHRONOUS;
     42	if (s_flags & SB_MANDLOCK)
     43		flags |= ST_MANDLOCK;
     44	if (s_flags & SB_RDONLY)
     45		flags |= ST_RDONLY;
     46	return flags;
     47}
     48
     49static int calculate_f_flags(struct vfsmount *mnt)
     50{
     51	return ST_VALID | flags_by_mnt(mnt->mnt_flags) |
     52		flags_by_sb(mnt->mnt_sb->s_flags);
     53}
     54
     55static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
     56{
     57	int retval;
     58
     59	if (!dentry->d_sb->s_op->statfs)
     60		return -ENOSYS;
     61
     62	memset(buf, 0, sizeof(*buf));
     63	retval = security_sb_statfs(dentry);
     64	if (retval)
     65		return retval;
     66	retval = dentry->d_sb->s_op->statfs(dentry, buf);
     67	if (retval == 0 && buf->f_frsize == 0)
     68		buf->f_frsize = buf->f_bsize;
     69	return retval;
     70}
     71
     72int vfs_get_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
     73{
     74	struct kstatfs st;
     75	int error;
     76
     77	error = statfs_by_dentry(dentry, &st);
     78	if (error)
     79		return error;
     80
     81	*fsid = st.f_fsid;
     82	return 0;
     83}
     84EXPORT_SYMBOL(vfs_get_fsid);
     85
     86int vfs_statfs(const struct path *path, struct kstatfs *buf)
     87{
     88	int error;
     89
     90	error = statfs_by_dentry(path->dentry, buf);
     91	if (!error)
     92		buf->f_flags = calculate_f_flags(path->mnt);
     93	return error;
     94}
     95EXPORT_SYMBOL(vfs_statfs);
     96
     97int user_statfs(const char __user *pathname, struct kstatfs *st)
     98{
     99	struct path path;
    100	int error;
    101	unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT;
    102retry:
    103	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
    104	if (!error) {
    105		error = vfs_statfs(&path, st);
    106		path_put(&path);
    107		if (retry_estale(error, lookup_flags)) {
    108			lookup_flags |= LOOKUP_REVAL;
    109			goto retry;
    110		}
    111	}
    112	return error;
    113}
    114
    115int fd_statfs(int fd, struct kstatfs *st)
    116{
    117	struct fd f = fdget_raw(fd);
    118	int error = -EBADF;
    119	if (f.file) {
    120		error = vfs_statfs(&f.file->f_path, st);
    121		fdput(f);
    122	}
    123	return error;
    124}
    125
    126static int do_statfs_native(struct kstatfs *st, struct statfs __user *p)
    127{
    128	struct statfs buf;
    129
    130	if (sizeof(buf) == sizeof(*st))
    131		memcpy(&buf, st, sizeof(*st));
    132	else {
    133		if (sizeof buf.f_blocks == 4) {
    134			if ((st->f_blocks | st->f_bfree | st->f_bavail |
    135			     st->f_bsize | st->f_frsize) &
    136			    0xffffffff00000000ULL)
    137				return -EOVERFLOW;
    138			/*
    139			 * f_files and f_ffree may be -1; it's okay to stuff
    140			 * that into 32 bits
    141			 */
    142			if (st->f_files != -1 &&
    143			    (st->f_files & 0xffffffff00000000ULL))
    144				return -EOVERFLOW;
    145			if (st->f_ffree != -1 &&
    146			    (st->f_ffree & 0xffffffff00000000ULL))
    147				return -EOVERFLOW;
    148		}
    149
    150		buf.f_type = st->f_type;
    151		buf.f_bsize = st->f_bsize;
    152		buf.f_blocks = st->f_blocks;
    153		buf.f_bfree = st->f_bfree;
    154		buf.f_bavail = st->f_bavail;
    155		buf.f_files = st->f_files;
    156		buf.f_ffree = st->f_ffree;
    157		buf.f_fsid = st->f_fsid;
    158		buf.f_namelen = st->f_namelen;
    159		buf.f_frsize = st->f_frsize;
    160		buf.f_flags = st->f_flags;
    161		memset(buf.f_spare, 0, sizeof(buf.f_spare));
    162	}
    163	if (copy_to_user(p, &buf, sizeof(buf)))
    164		return -EFAULT;
    165	return 0;
    166}
    167
    168static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p)
    169{
    170	struct statfs64 buf;
    171	if (sizeof(buf) == sizeof(*st))
    172		memcpy(&buf, st, sizeof(*st));
    173	else {
    174		buf.f_type = st->f_type;
    175		buf.f_bsize = st->f_bsize;
    176		buf.f_blocks = st->f_blocks;
    177		buf.f_bfree = st->f_bfree;
    178		buf.f_bavail = st->f_bavail;
    179		buf.f_files = st->f_files;
    180		buf.f_ffree = st->f_ffree;
    181		buf.f_fsid = st->f_fsid;
    182		buf.f_namelen = st->f_namelen;
    183		buf.f_frsize = st->f_frsize;
    184		buf.f_flags = st->f_flags;
    185		memset(buf.f_spare, 0, sizeof(buf.f_spare));
    186	}
    187	if (copy_to_user(p, &buf, sizeof(buf)))
    188		return -EFAULT;
    189	return 0;
    190}
    191
    192SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf)
    193{
    194	struct kstatfs st;
    195	int error = user_statfs(pathname, &st);
    196	if (!error)
    197		error = do_statfs_native(&st, buf);
    198	return error;
    199}
    200
    201SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf)
    202{
    203	struct kstatfs st;
    204	int error;
    205	if (sz != sizeof(*buf))
    206		return -EINVAL;
    207	error = user_statfs(pathname, &st);
    208	if (!error)
    209		error = do_statfs64(&st, buf);
    210	return error;
    211}
    212
    213SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf)
    214{
    215	struct kstatfs st;
    216	int error = fd_statfs(fd, &st);
    217	if (!error)
    218		error = do_statfs_native(&st, buf);
    219	return error;
    220}
    221
    222SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf)
    223{
    224	struct kstatfs st;
    225	int error;
    226
    227	if (sz != sizeof(*buf))
    228		return -EINVAL;
    229
    230	error = fd_statfs(fd, &st);
    231	if (!error)
    232		error = do_statfs64(&st, buf);
    233	return error;
    234}
    235
    236static int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
    237{
    238	struct super_block *s = user_get_super(dev, false);
    239	int err;
    240	if (!s)
    241		return -EINVAL;
    242
    243	err = statfs_by_dentry(s->s_root, sbuf);
    244	drop_super(s);
    245	return err;
    246}
    247
    248SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
    249{
    250	struct ustat tmp;
    251	struct kstatfs sbuf;
    252	int err = vfs_ustat(new_decode_dev(dev), &sbuf);
    253	if (err)
    254		return err;
    255
    256	memset(&tmp,0,sizeof(struct ustat));
    257	tmp.f_tfree = sbuf.f_bfree;
    258	if (IS_ENABLED(CONFIG_ARCH_32BIT_USTAT_F_TINODE))
    259		tmp.f_tinode = min_t(u64, sbuf.f_ffree, UINT_MAX);
    260	else
    261		tmp.f_tinode = sbuf.f_ffree;
    262
    263	return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0;
    264}
    265
    266#ifdef CONFIG_COMPAT
    267static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf)
    268{
    269	struct compat_statfs buf;
    270	if (sizeof ubuf->f_blocks == 4) {
    271		if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail |
    272		     kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
    273			return -EOVERFLOW;
    274		/* f_files and f_ffree may be -1; it's okay
    275		 * to stuff that into 32 bits */
    276		if (kbuf->f_files != 0xffffffffffffffffULL
    277		 && (kbuf->f_files & 0xffffffff00000000ULL))
    278			return -EOVERFLOW;
    279		if (kbuf->f_ffree != 0xffffffffffffffffULL
    280		 && (kbuf->f_ffree & 0xffffffff00000000ULL))
    281			return -EOVERFLOW;
    282	}
    283	memset(&buf, 0, sizeof(struct compat_statfs));
    284	buf.f_type = kbuf->f_type;
    285	buf.f_bsize = kbuf->f_bsize;
    286	buf.f_blocks = kbuf->f_blocks;
    287	buf.f_bfree = kbuf->f_bfree;
    288	buf.f_bavail = kbuf->f_bavail;
    289	buf.f_files = kbuf->f_files;
    290	buf.f_ffree = kbuf->f_ffree;
    291	buf.f_namelen = kbuf->f_namelen;
    292	buf.f_fsid.val[0] = kbuf->f_fsid.val[0];
    293	buf.f_fsid.val[1] = kbuf->f_fsid.val[1];
    294	buf.f_frsize = kbuf->f_frsize;
    295	buf.f_flags = kbuf->f_flags;
    296	if (copy_to_user(ubuf, &buf, sizeof(struct compat_statfs)))
    297		return -EFAULT;
    298	return 0;
    299}
    300
    301/*
    302 * The following statfs calls are copies of code from fs/statfs.c and
    303 * should be checked against those from time to time
    304 */
    305COMPAT_SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct compat_statfs __user *, buf)
    306{
    307	struct kstatfs tmp;
    308	int error = user_statfs(pathname, &tmp);
    309	if (!error)
    310		error = put_compat_statfs(buf, &tmp);
    311	return error;
    312}
    313
    314COMPAT_SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct compat_statfs __user *, buf)
    315{
    316	struct kstatfs tmp;
    317	int error = fd_statfs(fd, &tmp);
    318	if (!error)
    319		error = put_compat_statfs(buf, &tmp);
    320	return error;
    321}
    322
    323static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf)
    324{
    325	struct compat_statfs64 buf;
    326
    327	if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
    328		return -EOVERFLOW;
    329
    330	memset(&buf, 0, sizeof(struct compat_statfs64));
    331	buf.f_type = kbuf->f_type;
    332	buf.f_bsize = kbuf->f_bsize;
    333	buf.f_blocks = kbuf->f_blocks;
    334	buf.f_bfree = kbuf->f_bfree;
    335	buf.f_bavail = kbuf->f_bavail;
    336	buf.f_files = kbuf->f_files;
    337	buf.f_ffree = kbuf->f_ffree;
    338	buf.f_namelen = kbuf->f_namelen;
    339	buf.f_fsid.val[0] = kbuf->f_fsid.val[0];
    340	buf.f_fsid.val[1] = kbuf->f_fsid.val[1];
    341	buf.f_frsize = kbuf->f_frsize;
    342	buf.f_flags = kbuf->f_flags;
    343	if (copy_to_user(ubuf, &buf, sizeof(struct compat_statfs64)))
    344		return -EFAULT;
    345	return 0;
    346}
    347
    348int kcompat_sys_statfs64(const char __user * pathname, compat_size_t sz, struct compat_statfs64 __user * buf)
    349{
    350	struct kstatfs tmp;
    351	int error;
    352
    353	if (sz != sizeof(*buf))
    354		return -EINVAL;
    355
    356	error = user_statfs(pathname, &tmp);
    357	if (!error)
    358		error = put_compat_statfs64(buf, &tmp);
    359	return error;
    360}
    361
    362COMPAT_SYSCALL_DEFINE3(statfs64, const char __user *, pathname, compat_size_t, sz, struct compat_statfs64 __user *, buf)
    363{
    364	return kcompat_sys_statfs64(pathname, sz, buf);
    365}
    366
    367int kcompat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user * buf)
    368{
    369	struct kstatfs tmp;
    370	int error;
    371
    372	if (sz != sizeof(*buf))
    373		return -EINVAL;
    374
    375	error = fd_statfs(fd, &tmp);
    376	if (!error)
    377		error = put_compat_statfs64(buf, &tmp);
    378	return error;
    379}
    380
    381COMPAT_SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, compat_size_t, sz, struct compat_statfs64 __user *, buf)
    382{
    383	return kcompat_sys_fstatfs64(fd, sz, buf);
    384}
    385
    386/*
    387 * This is a copy of sys_ustat, just dealing with a structure layout.
    388 * Given how simple this syscall is that apporach is more maintainable
    389 * than the various conversion hacks.
    390 */
    391COMPAT_SYSCALL_DEFINE2(ustat, unsigned, dev, struct compat_ustat __user *, u)
    392{
    393	struct compat_ustat tmp;
    394	struct kstatfs sbuf;
    395	int err = vfs_ustat(new_decode_dev(dev), &sbuf);
    396	if (err)
    397		return err;
    398
    399	memset(&tmp, 0, sizeof(struct compat_ustat));
    400	tmp.f_tfree = sbuf.f_bfree;
    401	tmp.f_tinode = sbuf.f_ffree;
    402	if (copy_to_user(u, &tmp, sizeof(struct compat_ustat)))
    403		return -EFAULT;
    404	return 0;
    405}
    406#endif