#include <linux/buffer_head.h> /* for fsync_super() */
#include <linux/mount.h>
#include <linux/security.h>
+#include <linux/syscalls.h>
#include <linux/vfs.h>
#include <linux/writeback.h> /* for the emergency remount stuff */
#include <linux/idr.h>
+#include <linux/kobject.h>
+#include <linux/devpts_fs.h>
+#include <linux/proc_fs.h>
#include <asm/uaccess.h>
struct file_system_type *get_fs_type(const char *name);
LIST_HEAD(super_blocks);
-spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(sb_lock);
/**
* alloc_super - create new superblock
INIT_LIST_HEAD(&s->s_files);
INIT_LIST_HEAD(&s->s_instances);
INIT_HLIST_HEAD(&s->s_anon);
+ INIT_LIST_HEAD(&s->s_inodes);
init_rwsem(&s->s_umount);
sema_init(&s->s_lock, 1);
down_write(&s->s_umount);
s->dq_op = sb_dquot_ops;
s->s_qcop = sb_quotactl_ops;
s->s_op = &default_op;
+ s->s_time_gran = 1000000000;
}
out:
return s;
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
dput(root);
fsync_super(sb);
lock_super(sb);
- lock_kernel();
sb->s_flags &= ~MS_ACTIVE;
/* bad name - it should be evict_inodes() */
invalidate_inodes(sb);
+ lock_kernel();
if (sop->write_super && sb->s_dirt)
sop->write_super(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);
}
s->s_type = type;
strlcpy(s->s_id, type->name, sizeof(s->s_id));
- list_add(&s->s_list, super_blocks.prev);
+ list_add_tail(&s->s_list, &super_blocks);
list_add(&s->s_instances, &type->fs_supers);
spin_unlock(&sb_lock);
get_filesystem(type);
*/
static struct idr unnamed_dev_idr;
-static spinlock_t unnamed_dev_lock = SPIN_LOCK_UNLOCKED;/* protects the above */
+static DEFINE_SPINLOCK(unnamed_dev_lock);/* 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);
return (void *)s->s_bdev == data;
}
+static void bdev_uevent(struct block_device *bdev, enum kobject_action action)
+{
+ if (bdev->bd_disk) {
+ if (bdev->bd_part)
+ kobject_uevent(&bdev->bd_part->kobj, action, NULL);
+ else
+ kobject_uevent(&bdev->bd_disk->kobj, action, NULL);
+ }
+}
+
struct super_block *get_sb_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int))
up_write(&s->s_umount);
deactivate_super(s);
s = ERR_PTR(error);
- } else
+ } else {
s->s_flags |= MS_ACTIVE;
+ bdev_uevent(bdev, KOBJ_MOUNT);
+ }
}
return s;
void kill_block_super(struct super_block *sb)
{
struct block_device *bdev = sb->s_bdev;
+
+ bdev_uevent(bdev, KOBJ_UMOUNT);
generic_shutdown_super(sb);
set_blocksize(bdev, sb->s_old_blocksize);
close_bdev_excl(bdev);
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
struct file_system_type *type = get_fs_type(fstype);
- struct super_block *sb = ERR_PTR(-ENOMEM);
+ struct super_block *sb;
struct vfsmount *mnt;
int error;
char *secdata = NULL;
if (!type)
return ERR_PTR(-ENODEV);
+ sb = ERR_PTR(-EPERM);
+ if ((type->fs_flags & FS_BINARY_MOUNTDATA) &&
+ !capable(CAP_SYS_ADMIN) && !vx_ccaps(VXC_BINARY_MOUNT))
+ goto out;
+
+ sb = ERR_PTR(-ENOMEM);
mnt = alloc_vfsmnt(name);
if (!mnt)
goto out;
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;
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;
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);