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