407 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 HWJS_SUSPENDS 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 (fd_file(f)) {120 error = vfs_statfs(&fd_file(f)->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 memset(&buf, 0, sizeof(buf));134 if (sizeof buf.f_blocks == 4) {135 if ((st->f_blocks | st->f_bfree | st->f_bavail |136 st->f_bsize | st->f_frsize) &137 0xffffffff00000000ULL)138 return -EOVERFLOW;139 /*140 * f_files and f_ffree may be -1; it's okay to stuff141 * that into 32 bits142 */143 if (st->f_files != -1 &&144 (st->f_files & 0xffffffff00000000ULL))145 return -EOVERFLOW;146 if (st->f_ffree != -1 &&147 (st->f_ffree & 0xffffffff00000000ULL))148 return -EOVERFLOW;149 }150 151 buf.f_type = st->f_type;152 buf.f_bsize = st->f_bsize;153 buf.f_blocks = st->f_blocks;154 buf.f_bfree = st->f_bfree;155 buf.f_bavail = st->f_bavail;156 buf.f_files = st->f_files;157 buf.f_ffree = st->f_ffree;158 buf.f_fsid = st->f_fsid;159 buf.f_namelen = st->f_namelen;160 buf.f_frsize = st->f_frsize;161 buf.f_flags = st->f_flags;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 memset(&buf, 0, sizeof(buf));175 buf.f_type = st->f_type;176 buf.f_bsize = st->f_bsize;177 buf.f_blocks = st->f_blocks;178 buf.f_bfree = st->f_bfree;179 buf.f_bavail = st->f_bavail;180 buf.f_files = st->f_files;181 buf.f_ffree = st->f_ffree;182 buf.f_fsid = st->f_fsid;183 buf.f_namelen = st->f_namelen;184 buf.f_frsize = st->f_frsize;185 buf.f_flags = st->f_flags;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 HWJS_SUSPENDS 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 else261 tmp.f_tinode = sbuf.f_ffree;262 263 return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0;264}265 266#ifdef CONFIG_COMPAT267static 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 okay275 * to stuff that into 32 bits */276 if (kbuf->f_files != 0xffffffffffffffffULL277 && (kbuf->f_files & 0xffffffff00000000ULL))278 return -EOVERFLOW;279 if (kbuf->f_ffree != 0xffffffffffffffffULL280 && (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 and303 * should be checked against those from time to time304 */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 maintainable389 * 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#endif407