X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fsuper.c;h=998fc473e1579ee9e1bb99d063ec047655524aac;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=e62838940bc7f2343641b9c3ac288f190d04b5be;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/super.c b/fs/super.c index e62838940..998fc473e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -35,6 +35,8 @@ #include #include /* for the emergency remount stuff */ #include +#include +#include #include @@ -101,6 +103,42 @@ static inline void destroy_super(struct super_block *s) /* Superblock refcounting */ +/* + * Drop a superblock's refcount. Returns non-zero if the superblock was + * destroyed. The caller must hold sb_lock. + */ +int __put_super(struct super_block *sb) +{ + int ret = 0; + + if (!--sb->s_count) { + destroy_super(sb); + ret = 1; + } + return ret; +} + +/* + * Drop a superblock's refcount. + * Returns non-zero if the superblock is about to be destroyed and + * at least is already removed from super_blocks list, so if we are + * making a loop through super blocks then we need to restart. + * The caller must hold sb_lock. + */ +int __put_super_and_need_restart(struct super_block *sb) +{ + /* check for race with generic_shutdown_super() */ + if (list_empty(&sb->s_list)) { + /* super block is removed, need to restart... */ + __put_super(sb); + return 1; + } + /* can't be the last, since s_list is still in use */ + sb->s_count--; + BUG_ON(sb->s_count == 0); + return 0; +} + /** * put_super - drop a temporary reference to superblock * @s: superblock in question @@ -108,14 +146,14 @@ static inline void destroy_super(struct super_block *s) * Drops a temporary reference, frees superblock if there's no * references left. */ -static inline void put_super(struct super_block *s) +static void put_super(struct super_block *sb) { spin_lock(&sb_lock); - if (!--s->s_count) - destroy_super(s); + __put_super(sb); spin_unlock(&sb_lock); } + /** * deactivate_super - drop an active reference to superblock * @s: superblock to deactivate @@ -214,7 +252,8 @@ void generic_shutdown_super(struct super_block *sb) unlock_super(sb); } spin_lock(&sb_lock); - list_del(&sb->s_list); + /* should be initialized for __put_super_and_need_restart() */ + list_del_init(&sb->s_list); list_del(&sb->s_instances); spin_unlock(&sb_lock); up_write(&sb->s_umount); @@ -266,7 +305,8 @@ retry: return ERR_PTR(err); } s->s_type = type; - list_add(&s->s_list, super_blocks.prev); + strlcpy(s->s_id, type->name, sizeof(s->s_id)); + list_add_tail(&s->s_list, &super_blocks); list_add(&s->s_instances, &type->fs_supers); spin_unlock(&sb_lock); get_filesystem(type); @@ -553,14 +593,19 @@ static spinlock_t unnamed_dev_lock = SPIN_LOCK_UNLOCKED;/* protects the above */ int set_anon_super(struct super_block *s, void *data) { int dev; + int error; - spin_lock(&unnamed_dev_lock); - if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0) { - spin_unlock(&unnamed_dev_lock); + retry: + if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0) return -ENOMEM; - } - dev = idr_get_new(&unnamed_dev_idr, NULL); + spin_lock(&unnamed_dev_lock); + error = idr_get_new(&unnamed_dev_idr, NULL, &dev); spin_unlock(&unnamed_dev_lock); + if (error == -EAGAIN) + /* We raced and lost with another CPU. */ + goto retry; + else if (error) + return -EAGAIN; if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) { spin_lock(&unnamed_dev_lock); @@ -765,6 +810,13 @@ do_kern_mount(const char *fstype, int flags, const char *name, void *data) sb = type->get_sb(type, flags, name, data); if (IS_ERR(sb)) goto out_free_secdata; + + error = -EPERM; + if (!capable(CAP_SYS_ADMIN) && !sb->s_bdev && + (sb->s_magic != PROC_SUPER_MAGIC) && + (sb->s_magic != DEVPTS_SUPER_MAGIC)) + goto out_sb; + error = security_sb_kern_mount(sb, secdata); if (error) goto out_sb; @@ -772,6 +824,7 @@ do_kern_mount(const char *fstype, int flags, const char *name, void *data) mnt->mnt_root = dget(sb->s_root); mnt->mnt_mountpoint = sb->s_root; mnt->mnt_parent = mnt; + mnt->mnt_namespace = current->namespace; up_write(&sb->s_umount); put_filesystem(type); return mnt; @@ -788,6 +841,8 @@ out: return (struct vfsmount *)sb; } +EXPORT_SYMBOL_GPL(do_kern_mount); + struct vfsmount *kern_mount(struct file_system_type *type) { return do_kern_mount(type->name, 0, type->name, NULL);