From f47ec3f28354795f000c14bf18ed967ec81a3ec3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Nov 2011 21:15:42 -0500 Subject: trim fs/internal.h some stuff in there can actually become static; some belongs to pnode.h as it's a private interface between namespace.c and pnode.c... Signed-off-by: Al Viro --- fs/internal.h | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'fs/internal.h') diff --git a/fs/internal.h b/fs/internal.h index fe327c20af83..839d3f9e9173 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -20,14 +20,8 @@ struct path; * block_dev.c */ #ifdef CONFIG_BLOCK -extern struct super_block *blockdev_superblock; extern void __init bdev_cache_init(void); -static inline int sb_is_blkdev_sb(struct super_block *sb) -{ - return sb == blockdev_superblock; -} - extern int __sync_blockdev(struct block_device *bdev, int wait); #else @@ -35,11 +29,6 @@ static inline void bdev_cache_init(void) { } -static inline int sb_is_blkdev_sb(struct super_block *sb) -{ - return 0; -} - static inline int __sync_blockdev(struct block_device *bdev, int wait) { return 0; @@ -51,25 +40,14 @@ static inline int __sync_blockdev(struct block_device *bdev, int wait) */ extern void __init chrdev_init(void); -/* - * exec.c - */ -extern int check_unsafe_exec(struct linux_binprm *); - /* * namespace.c */ extern int copy_mount_options(const void __user *, unsigned long *); extern int copy_mount_string(const void __user *, char **); -extern unsigned int mnt_get_count(struct vfsmount *mnt); extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int); extern struct vfsmount *lookup_mnt(struct path *); -extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, - struct vfsmount *); -extern void release_mounts(struct list_head *); -extern void umount_tree(struct vfsmount *, int, struct list_head *); -extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); extern int finish_automount(struct vfsmount *, struct path *); extern void mnt_make_longterm(struct vfsmount *); @@ -98,8 +76,6 @@ extern struct file *get_empty_filp(void); */ extern int do_remount_sb(struct super_block *, int, void *, int); extern bool grab_super_passive(struct super_block *sb); -extern void __put_super(struct super_block *sb); -extern void put_super(struct super_block *sb); extern struct dentry *mount_fs(struct file_system_type *, int, const char *, void *); -- cgit From cf31e70d6cf93f19fe9bf1144966ef40991ac723 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 2 Jan 2012 22:28:36 -0500 Subject: vfs: new helper - vfs_ustat() ... and bury user_get_super()/statfs_by_dentry() - they are purely internal now. Signed-off-by: Al Viro --- arch/parisc/hpux/sys_hpux.c | 9 +-------- fs/compat.c | 9 +-------- fs/internal.h | 1 + fs/statfs.c | 21 +++++++++++++-------- include/linux/fs.h | 3 +-- 5 files changed, 17 insertions(+), 26 deletions(-) (limited to 'fs/internal.h') diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c index 6ab9580b0b00..d9dc6cd3b7d2 100644 --- a/arch/parisc/hpux/sys_hpux.c +++ b/arch/parisc/hpux/sys_hpux.c @@ -136,16 +136,9 @@ struct hpux_ustat { */ static int hpux_ustat(dev_t dev, struct hpux_ustat __user *ubuf) { - struct super_block *s; struct hpux_ustat tmp; /* Changed to hpux_ustat */ struct kstatfs sbuf; - int err = -EINVAL; - - s = user_get_super(dev); - if (s == NULL) - goto out; - err = statfs_by_dentry(s->s_root, &sbuf); - drop_super(s); + int err = vfs_ustat(dev, &sbuf); if (err) goto out; diff --git a/fs/compat.c b/fs/compat.c index c98787536bb8..9db5a6076610 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -342,16 +342,9 @@ asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct c */ asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u) { - struct super_block *sb; struct compat_ustat tmp; struct kstatfs sbuf; - int err; - - sb = user_get_super(new_decode_dev(dev)); - if (!sb) - return -EINVAL; - err = statfs_by_dentry(sb->s_root, &sbuf); - drop_super(sb); + int err = vfs_ustat(new_decode_dev(dev), &sbuf); if (err) return err; diff --git a/fs/internal.h b/fs/internal.h index 839d3f9e9173..7b1cb1528ac2 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -78,6 +78,7 @@ extern int do_remount_sb(struct super_block *, int, void *, int); extern bool grab_super_passive(struct super_block *sb); extern struct dentry *mount_fs(struct file_system_type *, int, const char *, void *); +extern struct super_block *user_get_super(dev_t); /* * open.c diff --git a/fs/statfs.c b/fs/statfs.c index 9cf04a118965..2aa6a22e0be2 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -7,6 +7,7 @@ #include #include #include +#include "internal.h" static int flags_by_mnt(int mnt_flags) { @@ -45,7 +46,7 @@ static int calculate_f_flags(struct vfsmount *mnt) flags_by_sb(mnt->mnt_sb->s_flags); } -int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) +static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) { int retval; @@ -205,19 +206,23 @@ SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user return error; } -SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf) +int vfs_ustat(dev_t dev, struct kstatfs *sbuf) { - struct super_block *s; - struct ustat tmp; - struct kstatfs sbuf; + struct super_block *s = user_get_super(dev); int err; - - s = user_get_super(new_decode_dev(dev)); if (!s) return -EINVAL; - err = statfs_by_dentry(s->s_root, &sbuf); + 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; diff --git a/include/linux/fs.h b/include/linux/fs.h index ed17e54fd204..cec429d76ab0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1939,7 +1939,7 @@ extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *, extern int vfs_statfs(struct path *, struct kstatfs *); extern int user_statfs(const char __user *, struct kstatfs *); extern int fd_statfs(int, struct kstatfs *); -extern int statfs_by_dentry(struct dentry *, struct kstatfs *); +extern int vfs_ustat(dev_t, struct kstatfs *); extern int freeze_super(struct super_block *super); extern int thaw_super(struct super_block *super); extern bool our_mnt(struct vfsmount *mnt); @@ -2531,7 +2531,6 @@ extern void put_filesystem(struct file_system_type *fs); extern struct file_system_type *get_fs_type(const char *name); extern struct super_block *get_super(struct block_device *); extern struct super_block *get_active_super(struct block_device *bdev); -extern struct super_block *user_get_super(dev_t); extern void drop_super(struct super_block *sb); extern void iterate_supers(void (*)(struct super_block *, void *), void *); extern void iterate_supers_type(struct file_system_type *, -- cgit From a218d0fdc5f9004164ff151d274487f6799907d0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Nov 2011 14:59:34 -0500 Subject: switch open and mkdir syscalls to umode_t Signed-off-by: Al Viro --- fs/compat.c | 4 ++-- fs/internal.h | 2 +- fs/namei.c | 6 +++--- fs/open.c | 12 ++++++------ include/linux/compat.h | 4 ++-- include/linux/fs.h | 4 ++-- include/linux/syscalls.h | 10 +++++----- 7 files changed, 21 insertions(+), 21 deletions(-) (limited to 'fs/internal.h') diff --git a/fs/compat.c b/fs/compat.c index 9db5a6076610..fa9d721ecfee 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1281,7 +1281,7 @@ compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32, * O_LARGEFILE flag. */ asmlinkage long -compat_sys_open(const char __user *filename, int flags, int mode) +compat_sys_open(const char __user *filename, int flags, umode_t mode) { return do_sys_open(AT_FDCWD, filename, flags, mode); } @@ -1291,7 +1291,7 @@ compat_sys_open(const char __user *filename, int flags, int mode) * O_LARGEFILE flag. */ asmlinkage long -compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode) +compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, umode_t mode) { return do_sys_open(dfd, filename, flags, mode); } diff --git a/fs/internal.h b/fs/internal.h index 7b1cb1528ac2..23599f88d1a5 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -88,7 +88,7 @@ extern struct file *nameidata_to_filp(struct nameidata *); extern void release_open_intent(struct nameidata *); struct open_flags { int open_flag; - int mode; + umode_t mode; int acc_mode; int intent; }; diff --git a/fs/namei.c b/fs/namei.c index e275dc36d7c5..afd5876cd072 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2177,7 +2177,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, /* Negative dentry, just create the file */ if (!dentry->d_inode) { - int mode = op->mode; + umode_t mode = op->mode; if (!IS_POSIXACL(dir->d_inode)) mode &= ~current_umask(); /* @@ -2562,7 +2562,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) return error; } -SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode) +SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) { struct dentry *dentry; struct path path; @@ -2590,7 +2590,7 @@ out_dput: return error; } -SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode) +SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) { return sys_mkdirat(AT_FDCWD, pathname, mode); } diff --git a/fs/open.c b/fs/open.c index 834e3e1adeb9..2659f596f4c5 100644 --- a/fs/open.c +++ b/fs/open.c @@ -877,7 +877,7 @@ void fd_install(unsigned int fd, struct file *file) EXPORT_SYMBOL(fd_install); -static inline int build_open_flags(int flags, int mode, struct open_flags *op) +static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op) { int lookup_flags = 0; int acc_mode; @@ -948,7 +948,7 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op) * have to. But in generally you should not do this, so please move * along, nothing to see here.. */ -struct file *filp_open(const char *filename, int flags, int mode) +struct file *filp_open(const char *filename, int flags, umode_t mode) { struct open_flags op; int lookup = build_open_flags(flags, mode, &op); @@ -970,7 +970,7 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(file_open_root); -long do_sys_open(int dfd, const char __user *filename, int flags, int mode) +long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; int lookup = build_open_flags(flags, mode, &op); @@ -994,7 +994,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) return fd; } -SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) +SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) { long ret; @@ -1008,7 +1008,7 @@ SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) } SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, - int, mode) + umode_t, mode) { long ret; @@ -1027,7 +1027,7 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -SYSCALL_DEFINE2(creat, const char __user *, pathname, int, mode) +SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode) { return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } diff --git a/include/linux/compat.h b/include/linux/compat.h index 66ed067fb729..41c9f6515f46 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -422,9 +422,9 @@ asmlinkage long compat_sys_getdents64(unsigned int fd, asmlinkage long compat_sys_vmsplice(int fd, const struct compat_iovec __user *, unsigned int nr_segs, unsigned int flags); asmlinkage long compat_sys_open(const char __user *filename, int flags, - int mode); + umode_t mode); asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, - int flags, int mode); + int flags, umode_t mode); asmlinkage long compat_sys_open_by_handle_at(int mountdirfd, struct file_handle __user *handle, int flags); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d02fab420c6..f0e57b7e4297 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2054,8 +2054,8 @@ extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, extern int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len); extern long do_sys_open(int dfd, const char __user *filename, int flags, - int mode); -extern struct file *filp_open(const char *, int, int); + umode_t mode); +extern struct file *filp_open(const char *, int, umode_t); extern struct file *file_open_root(struct dentry *, struct vfsmount *, const char *, int); extern struct file * dentry_open(struct dentry *, struct vfsmount *, int, diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index b25621476316..515669fa3c1d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -517,9 +517,9 @@ asmlinkage long sys_sendfile64(int out_fd, int in_fd, loff_t __user *offset, size_t count); asmlinkage long sys_readlink(const char __user *path, char __user *buf, int bufsiz); -asmlinkage long sys_creat(const char __user *pathname, int mode); +asmlinkage long sys_creat(const char __user *pathname, umode_t mode); asmlinkage long sys_open(const char __user *filename, - int flags, int mode); + int flags, umode_t mode); asmlinkage long sys_close(unsigned int fd); asmlinkage long sys_access(const char __user *filename, int mode); asmlinkage long sys_vhangup(void); @@ -582,7 +582,7 @@ asmlinkage long sys_preadv(unsigned long fd, const struct iovec __user *vec, asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h); asmlinkage long sys_getcwd(char __user *buf, unsigned long size); -asmlinkage long sys_mkdir(const char __user *pathname, int mode); +asmlinkage long sys_mkdir(const char __user *pathname, umode_t mode); asmlinkage long sys_chdir(const char __user *filename); asmlinkage long sys_fchdir(unsigned int fd); asmlinkage long sys_rmdir(const char __user *pathname); @@ -757,7 +757,7 @@ asmlinkage long sys_spu_create(const char __user *name, asmlinkage long sys_mknodat(int dfd, const char __user * filename, umode_t mode, unsigned dev); -asmlinkage long sys_mkdirat(int dfd, const char __user * pathname, int mode); +asmlinkage long sys_mkdirat(int dfd, const char __user * pathname, umode_t mode); asmlinkage long sys_unlinkat(int dfd, const char __user * pathname, int flag); asmlinkage long sys_symlinkat(const char __user * oldname, int newdfd, const char __user * newname); @@ -773,7 +773,7 @@ asmlinkage long sys_fchmodat(int dfd, const char __user * filename, asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, - int mode); + umode_t mode); asmlinkage long sys_newfstatat(int dfd, const char __user *filename, struct stat __user *statbuf, int flag); asmlinkage long sys_fstatat64(int dfd, const char __user *filename, -- cgit From c71053659e3bb27d44b79da0bb4abf5838c2060a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Nov 2011 18:22:03 -0500 Subject: vfs: spread struct mount - __lookup_mnt() result switch __lookup_mnt() to returning struct mount *; callers adjusted. Signed-off-by: Al Viro --- fs/internal.h | 2 +- fs/mount.h | 2 ++ fs/namei.c | 13 +++++++------ fs/namespace.c | 23 ++++++++++++++--------- fs/pnode.c | 13 +++++++------ 5 files changed, 31 insertions(+), 22 deletions(-) (limited to 'fs/internal.h') diff --git a/fs/internal.h b/fs/internal.h index 7b1cb1528ac2..6aab61a4f36f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -15,6 +15,7 @@ struct super_block; struct file_system_type; struct linux_binprm; struct path; +struct mount; /* * block_dev.c @@ -46,7 +47,6 @@ extern void __init chrdev_init(void); extern int copy_mount_options(const void __user *, unsigned long *); extern int copy_mount_string(const void __user *, char **); -extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int); extern struct vfsmount *lookup_mnt(struct path *); extern int finish_automount(struct vfsmount *, struct path *); diff --git a/fs/mount.h b/fs/mount.h index 47da8163e1f4..44e5b6f54b7e 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -13,3 +13,5 @@ static inline int mnt_has_parent(struct vfsmount *mnt) { return mnt != mnt->mnt_parent; } + +extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *, int); diff --git a/fs/namei.c b/fs/namei.c index 5008f01787f5..d1c6a559f8f0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -36,6 +36,7 @@ #include #include "internal.h" +#include "mount.h" /* [Feb-1997 T. Schoebel-Theuer] * Fundamental changes in the pathname lookup mechanisms (namei) @@ -884,7 +885,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, struct inode **inode) { for (;;) { - struct vfsmount *mounted; + struct mount *mounted; /* * Don't forget we might have a non-mountpoint managed dentry * that wants to block transit. @@ -898,8 +899,8 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, mounted = __lookup_mnt(path->mnt, path->dentry, 1); if (!mounted) break; - path->mnt = mounted; - path->dentry = mounted->mnt_root; + path->mnt = &mounted->mnt; + path->dentry = mounted->mnt.mnt_root; nd->flags |= LOOKUP_JUMPED; nd->seq = read_seqcount_begin(&path->dentry->d_seq); /* @@ -915,12 +916,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, static void follow_mount_rcu(struct nameidata *nd) { while (d_mountpoint(nd->path.dentry)) { - struct vfsmount *mounted; + struct mount *mounted; mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry, 1); if (!mounted) break; - nd->path.mnt = mounted; - nd->path.dentry = mounted->mnt_root; + nd->path.mnt = &mounted->mnt; + nd->path.dentry = mounted->mnt.mnt_root; nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); } } diff --git a/fs/namespace.c b/fs/namespace.c index dda47fee6fdf..398eb2421494 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -464,20 +464,20 @@ static void free_vfsmnt(struct vfsmount *mnt) * @dir. If @dir is set return the first mount else return the last mount. * vfsmount_lock must be held for read or write. */ -struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, +struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, int dir) { struct list_head *head = mount_hashtable + hash(mnt, dentry); struct list_head *tmp = head; - struct vfsmount *p, *found = NULL; + struct mount *p, *found = NULL; for (;;) { tmp = dir ? tmp->next : tmp->prev; p = NULL; if (tmp == head) break; - p = list_entry(tmp, struct vfsmount, mnt_hash); - if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { + p = list_entry(tmp, struct mount, mnt.mnt_hash); + if (p->mnt.mnt_parent == mnt && p->mnt.mnt_mountpoint == dentry) { found = p; break; } @@ -491,13 +491,18 @@ struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, */ struct vfsmount *lookup_mnt(struct path *path) { - struct vfsmount *child_mnt; + struct mount *child_mnt; br_read_lock(vfsmount_lock); - if ((child_mnt = __lookup_mnt(path->mnt, path->dentry, 1))) - mntget(child_mnt); - br_read_unlock(vfsmount_lock); - return child_mnt; + child_mnt = __lookup_mnt(path->mnt, path->dentry, 1); + if (child_mnt) { + mnt_add_count(child_mnt, 1); + br_read_unlock(vfsmount_lock); + return &child_mnt->mnt; + } else { + br_read_unlock(vfsmount_lock); + return NULL; + } } static inline int check_mnt(struct vfsmount *mnt) diff --git a/fs/pnode.c b/fs/pnode.c index 4d5a06ea57a2..e996d039c0f2 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -289,7 +289,8 @@ static inline int do_refcount_check(struct vfsmount *mnt, int count) */ int propagate_mount_busy(struct vfsmount *mnt, int refcnt) { - struct vfsmount *m, *child; + struct vfsmount *m; + struct mount *child; struct vfsmount *parent = mnt->mnt_parent; int ret = 0; @@ -307,8 +308,8 @@ int propagate_mount_busy(struct vfsmount *mnt, int refcnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { child = __lookup_mnt(m, mnt->mnt_mountpoint, 0); - if (child && list_empty(&child->mnt_mounts) && - (ret = do_refcount_check(child, 1))) + if (child && list_empty(&child->mnt.mnt_mounts) && + (ret = do_refcount_check(&child->mnt, 1))) break; } return ret; @@ -328,14 +329,14 @@ static void __propagate_umount(struct vfsmount *mnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { - struct vfsmount *child = __lookup_mnt(m, + struct mount *child = __lookup_mnt(m, mnt->mnt_mountpoint, 0); /* * umount the child only if the child has no * other children */ - if (child && list_empty(&child->mnt_mounts)) - list_move_tail(&child->mnt_hash, &mnt->mnt_hash); + if (child && list_empty(&child->mnt.mnt_mounts)) + list_move_tail(&child->mnt.mnt_hash, &mnt->mnt_hash); } } -- cgit From 4ed5e82fe77f4147cf386327c9a63a2dd7eff518 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 21 Nov 2011 12:11:31 +0100 Subject: vfs: protect remounting superblock read-only Currently remouting superblock read-only is racy in a major way. With the per mount read-only infrastructure it is now possible to prevent most races, which this patch attempts. Before starting the remount read-only, iterate through all mounts belonging to the superblock and if none of them have any pending writes, set sb->s_readonly_remount. This indicates that remount is in progress and no further write requests are allowed. If the remount succeeds set MS_RDONLY and reset s_readonly_remount. If the remounting is unsuccessful just reset s_readonly_remount. This can result in transient EROFS errors, despite the fact the remount failed. Unfortunately hodling off writes is difficult as remount itself may touch the filesystem (e.g. through load_nls()) which would deadlock. A later patch deals with delayed writes due to nlink going to zero. Signed-off-by: Miklos Szeredi Tested-by: Toshiyuki Okajima Signed-off-by: Al Viro --- fs/internal.h | 1 + fs/namespace.c | 40 +++++++++++++++++++++++++++++++++++++++- fs/super.c | 22 ++++++++++++++++++---- include/linux/fs.h | 3 +++ 4 files changed, 61 insertions(+), 5 deletions(-) (limited to 'fs/internal.h') diff --git a/fs/internal.h b/fs/internal.h index 2523a4029452..9962c59ba280 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -52,6 +52,7 @@ extern int finish_automount(struct vfsmount *, struct path *); extern void mnt_make_longterm(struct vfsmount *); extern void mnt_make_shortterm(struct vfsmount *); +extern int sb_prepare_remount_readonly(struct super_block *); extern void __init mnt_init(void); diff --git a/fs/namespace.c b/fs/namespace.c index 145217b088d1..98ebc78b21ab 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -273,6 +273,15 @@ static unsigned int mnt_get_writers(struct mount *mnt) #endif } +static int mnt_is_readonly(struct vfsmount *mnt) +{ + if (mnt->mnt_sb->s_readonly_remount) + return 1; + /* Order wrt setting s_flags/s_readonly_remount in do_remount() */ + smp_rmb(); + return __mnt_is_readonly(mnt); +} + /* * Most r/o checks on a fs are for operations that take * discrete amounts of time, like a write() or unlink(). @@ -312,7 +321,7 @@ int mnt_want_write(struct vfsmount *m) * MNT_WRITE_HOLD is cleared. */ smp_rmb(); - if (__mnt_is_readonly(m)) { + if (mnt_is_readonly(m)) { mnt_dec_writers(mnt); ret = -EROFS; goto out; @@ -435,6 +444,35 @@ static void __mnt_unmake_readonly(struct mount *mnt) br_write_unlock(vfsmount_lock); } +int sb_prepare_remount_readonly(struct super_block *sb) +{ + struct mount *mnt; + int err = 0; + + br_write_lock(vfsmount_lock); + list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) { + if (!(mnt->mnt.mnt_flags & MNT_READONLY)) { + mnt->mnt.mnt_flags |= MNT_WRITE_HOLD; + smp_mb(); + if (mnt_get_writers(mnt) > 0) { + err = -EBUSY; + break; + } + } + } + if (!err) { + sb->s_readonly_remount = 1; + smp_wmb(); + } + list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) { + if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD) + mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD; + } + br_write_unlock(vfsmount_lock); + + return err; +} + static void free_vfsmnt(struct mount *mnt) { kfree(mnt->mnt_devname); diff --git a/fs/super.c b/fs/super.c index 993ca8f128d6..6acc02237e3e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -723,23 +723,33 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) /* If we are remounting RDONLY and current sb is read/write, make sure there are no rw files opened */ if (remount_ro) { - if (force) + if (force) { mark_files_ro(sb); - else if (!fs_may_remount_ro(sb)) - return -EBUSY; + } else { + retval = sb_prepare_remount_readonly(sb); + if (retval) + return retval; + + retval = -EBUSY; + if (!fs_may_remount_ro(sb)) + goto cancel_readonly; + } } if (sb->s_op->remount_fs) { retval = sb->s_op->remount_fs(sb, &flags, data); if (retval) { if (!force) - return retval; + goto cancel_readonly; /* If forced remount, go ahead despite any errors */ WARN(1, "forced remount of a %s fs returned %i\n", sb->s_type->name, retval); } } sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK); + /* Needs to be ordered wrt mnt_is_readonly() */ + smp_wmb(); + sb->s_readonly_remount = 0; /* * Some filesystems modify their metadata via some other path than the @@ -752,6 +762,10 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) if (remount_ro && sb->s_bdev) invalidate_bdev(sb->s_bdev); return 0; + +cancel_readonly: + sb->s_readonly_remount = 0; + return retval; } static void do_emergency_remount(struct work_struct *work) diff --git a/include/linux/fs.h b/include/linux/fs.h index 03385acd71e8..7b8a681b1ef4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1482,6 +1482,9 @@ struct super_block { int cleancache_poolid; struct shrinker s_shrink; /* per-sb shrinker handle */ + + /* Being remounted read-only */ + int s_readonly_remount; }; /* superblock cache pruning functions */ -- cgit