statfs.c 5.32 KB
Newer Older
1
#include <linux/syscalls.h>
2
#include <linux/export.h>
3 4
#include <linux/fs.h>
#include <linux/file.h>
5
#include <linux/mount.h>
6 7 8 9
#include <linux/namei.h>
#include <linux/statfs.h>
#include <linux/security.h>
#include <linux/uaccess.h>
Al Viro's avatar
Al Viro committed
10
#include "internal.h"
11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
static int flags_by_mnt(int mnt_flags)
{
	int flags = 0;

	if (mnt_flags & MNT_READONLY)
		flags |= ST_RDONLY;
	if (mnt_flags & MNT_NOSUID)
		flags |= ST_NOSUID;
	if (mnt_flags & MNT_NODEV)
		flags |= ST_NODEV;
	if (mnt_flags & MNT_NOEXEC)
		flags |= ST_NOEXEC;
	if (mnt_flags & MNT_NOATIME)
		flags |= ST_NOATIME;
	if (mnt_flags & MNT_NODIRATIME)
		flags |= ST_NODIRATIME;
	if (mnt_flags & MNT_RELATIME)
		flags |= ST_RELATIME;
	return flags;
}

static int flags_by_sb(int s_flags)
{
	int flags = 0;
	if (s_flags & MS_SYNCHRONOUS)
		flags |= ST_SYNCHRONOUS;
	if (s_flags & MS_MANDLOCK)
		flags |= ST_MANDLOCK;
	return flags;
}

static int calculate_f_flags(struct vfsmount *mnt)
{
	return ST_VALID | flags_by_mnt(mnt->mnt_flags) |
		flags_by_sb(mnt->mnt_sb->s_flags);
}

Al Viro's avatar
Al Viro committed
49
static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
50
{
51 52 53 54 55 56 57 58 59 60 61 62
	int retval;

	if (!dentry->d_sb->s_op->statfs)
		return -ENOSYS;

	memset(buf, 0, sizeof(*buf));
	retval = security_sb_statfs(dentry);
	if (retval)
		return retval;
	retval = dentry->d_sb->s_op->statfs(dentry, buf);
	if (retval == 0 && buf->f_frsize == 0)
		buf->f_frsize = buf->f_bsize;
63 64 65
	return retval;
}

66 67
int vfs_statfs(struct path *path, struct kstatfs *buf)
{
68 69 70 71 72 73
	int error;

	error = statfs_by_dentry(path->dentry, buf);
	if (!error)
		buf->f_flags = calculate_f_flags(path->mnt);
	return error;
74
}
75 76
EXPORT_SYMBOL(vfs_statfs);

Al Viro's avatar
Al Viro committed
77
int user_statfs(const char __user *pathname, struct kstatfs *st)
78
{
Al Viro's avatar
Al Viro committed
79
	struct path path;
80 81 82 83
	int error;
	unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT;
retry:
	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
Al Viro's avatar
Al Viro committed
84 85 86
	if (!error) {
		error = vfs_statfs(&path, st);
		path_put(&path);
87 88 89 90
		if (retry_estale(error, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
Al Viro's avatar
Al Viro committed
91 92 93
	}
	return error;
}
94

Al Viro's avatar
Al Viro committed
95 96
int fd_statfs(int fd, struct kstatfs *st)
{
97
	struct fd f = fdget_raw(fd);
Al Viro's avatar
Al Viro committed
98
	int error = -EBADF;
99 100 101
	if (f.file) {
		error = vfs_statfs(&f.file->f_path, st);
		fdput(f);
Al Viro's avatar
Al Viro committed
102 103 104
	}
	return error;
}
105

Al Viro's avatar
Al Viro committed
106 107 108 109 110 111
static int do_statfs_native(struct kstatfs *st, struct statfs __user *p)
{
	struct statfs buf;

	if (sizeof(buf) == sizeof(*st))
		memcpy(&buf, st, sizeof(*st));
112
	else {
Al Viro's avatar
Al Viro committed
113 114 115
		if (sizeof buf.f_blocks == 4) {
			if ((st->f_blocks | st->f_bfree | st->f_bavail |
			     st->f_bsize | st->f_frsize) &
116 117 118 119 120 121
			    0xffffffff00000000ULL)
				return -EOVERFLOW;
			/*
			 * f_files and f_ffree may be -1; it's okay to stuff
			 * that into 32 bits
			 */
Al Viro's avatar
Al Viro committed
122 123
			if (st->f_files != -1 &&
			    (st->f_files & 0xffffffff00000000ULL))
124
				return -EOVERFLOW;
Al Viro's avatar
Al Viro committed
125 126
			if (st->f_ffree != -1 &&
			    (st->f_ffree & 0xffffffff00000000ULL))
127 128 129
				return -EOVERFLOW;
		}

Al Viro's avatar
Al Viro committed
130 131 132 133 134 135 136 137 138 139 140 141
		buf.f_type = st->f_type;
		buf.f_bsize = st->f_bsize;
		buf.f_blocks = st->f_blocks;
		buf.f_bfree = st->f_bfree;
		buf.f_bavail = st->f_bavail;
		buf.f_files = st->f_files;
		buf.f_ffree = st->f_ffree;
		buf.f_fsid = st->f_fsid;
		buf.f_namelen = st->f_namelen;
		buf.f_frsize = st->f_frsize;
		buf.f_flags = st->f_flags;
		memset(buf.f_spare, 0, sizeof(buf.f_spare));
142
	}
Al Viro's avatar
Al Viro committed
143 144
	if (copy_to_user(p, &buf, sizeof(buf)))
		return -EFAULT;
145 146 147
	return 0;
}

Al Viro's avatar
Al Viro committed
148
static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p)
149
{
Al Viro's avatar
Al Viro committed
150 151 152
	struct statfs64 buf;
	if (sizeof(buf) == sizeof(*st))
		memcpy(&buf, st, sizeof(*st));
153
	else {
Al Viro's avatar
Al Viro committed
154 155 156 157 158 159 160 161 162 163 164 165
		buf.f_type = st->f_type;
		buf.f_bsize = st->f_bsize;
		buf.f_blocks = st->f_blocks;
		buf.f_bfree = st->f_bfree;
		buf.f_bavail = st->f_bavail;
		buf.f_files = st->f_files;
		buf.f_ffree = st->f_ffree;
		buf.f_fsid = st->f_fsid;
		buf.f_namelen = st->f_namelen;
		buf.f_frsize = st->f_frsize;
		buf.f_flags = st->f_flags;
		memset(buf.f_spare, 0, sizeof(buf.f_spare));
166
	}
Al Viro's avatar
Al Viro committed
167 168
	if (copy_to_user(p, &buf, sizeof(buf)))
		return -EFAULT;
169 170 171 172 173
	return 0;
}

SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf)
{
Al Viro's avatar
Al Viro committed
174 175 176 177
	struct kstatfs st;
	int error = user_statfs(pathname, &st);
	if (!error)
		error = do_statfs_native(&st, buf);
178 179 180 181 182
	return error;
}

SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf)
{
Al Viro's avatar
Al Viro committed
183 184
	struct kstatfs st;
	int error;
185 186
	if (sz != sizeof(*buf))
		return -EINVAL;
Al Viro's avatar
Al Viro committed
187 188 189
	error = user_statfs(pathname, &st);
	if (!error)
		error = do_statfs64(&st, buf);
190 191 192 193 194
	return error;
}

SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf)
{
Al Viro's avatar
Al Viro committed
195 196 197 198
	struct kstatfs st;
	int error = fd_statfs(fd, &st);
	if (!error)
		error = do_statfs_native(&st, buf);
199 200 201 202 203
	return error;
}

SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf)
{
Al Viro's avatar
Al Viro committed
204
	struct kstatfs st;
205 206 207 208 209
	int error;

	if (sz != sizeof(*buf))
		return -EINVAL;

Al Viro's avatar
Al Viro committed
210 211 212
	error = fd_statfs(fd, &st);
	if (!error)
		error = do_statfs64(&st, buf);
213 214 215
	return error;
}

Al Viro's avatar
Al Viro committed
216
int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
217
{
Al Viro's avatar
Al Viro committed
218
	struct super_block *s = user_get_super(dev);
219 220 221 222
	int err;
	if (!s)
		return -EINVAL;

Al Viro's avatar
Al Viro committed
223
	err = statfs_by_dentry(s->s_root, sbuf);
224
	drop_super(s);
Al Viro's avatar
Al Viro committed
225 226 227 228 229 230 231 232
	return err;
}

SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
{
	struct ustat tmp;
	struct kstatfs sbuf;
	int err = vfs_ustat(new_decode_dev(dev), &sbuf);
233 234 235 236 237 238 239 240 241
	if (err)
		return err;

	memset(&tmp,0,sizeof(struct ustat));
	tmp.f_tfree = sbuf.f_bfree;
	tmp.f_tinode = sbuf.f_ffree;

	return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0;
}