241 lines · 5.3 KB · C
RawDownload
#include <linux/syscalls.h>#include <linux/export.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/mount.h>#include <linux/namei.h>#include <linux/statfs.h>#include <linux/security.h>#include <linux/uaccess.h>#include "internal.h" 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);} static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf){	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;	return retval;} int vfs_statfs(struct path *path, struct kstatfs *buf){	int error; 	error = statfs_by_dentry(path->dentry, buf);	if (!error)		buf->f_flags = calculate_f_flags(path->mnt);	return error;}EXPORT_SYMBOL(vfs_statfs); int user_statfs(const char __user *pathname, struct kstatfs *st){	struct path path;	int error;	unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT;retry:	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);	if (!error) {		error = vfs_statfs(&path, st);		path_put(&path);		if (retry_estale(error, lookup_flags)) {			lookup_flags |= LOOKUP_REVAL;			goto retry;		}	}	return error;} int fd_statfs(int fd, struct kstatfs *st){	struct fd f = fdget_raw(fd);	int error = -EBADF;	if (f.file) {		error = vfs_statfs(&f.file->f_path, st);		fdput(f);	}	return error;} 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));	else {		if (sizeof buf.f_blocks == 4) {			if ((st->f_blocks | st->f_bfree | st->f_bavail |			     st->f_bsize | st->f_frsize) &			    0xffffffff00000000ULL)				return -EOVERFLOW;			/*			 * f_files and f_ffree may be -1; it's okay to stuff			 * that into 32 bits			 */			if (st->f_files != -1 &&			    (st->f_files & 0xffffffff00000000ULL))				return -EOVERFLOW;			if (st->f_ffree != -1 &&			    (st->f_ffree & 0xffffffff00000000ULL))				return -EOVERFLOW;		} 		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));	}	if (copy_to_user(p, &buf, sizeof(buf)))		return -EFAULT;	return 0;} static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p){	struct statfs64 buf;	if (sizeof(buf) == sizeof(*st))		memcpy(&buf, st, sizeof(*st));	else {		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));	}	if (copy_to_user(p, &buf, sizeof(buf)))		return -EFAULT;	return 0;} SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf){	struct kstatfs st;	int error = user_statfs(pathname, &st);	if (!error)		error = do_statfs_native(&st, buf);	return error;} SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf){	struct kstatfs st;	int error;	if (sz != sizeof(*buf))		return -EINVAL;	error = user_statfs(pathname, &st);	if (!error)		error = do_statfs64(&st, buf);	return error;} SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf){	struct kstatfs st;	int error = fd_statfs(fd, &st);	if (!error)		error = do_statfs_native(&st, buf);	return error;} SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf){	struct kstatfs st;	int error; 	if (sz != sizeof(*buf))		return -EINVAL; 	error = fd_statfs(fd, &st);	if (!error)		error = do_statfs64(&st, buf);	return error;} int vfs_ustat(dev_t dev, struct kstatfs *sbuf){	struct super_block *s = user_get_super(dev);	int err;	if (!s)		return -EINVAL; 	err = statfs_by_dentry(s->s_root, sbuf);	drop_super(s);	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);	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;}