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