struct super_block *old;
int err;
- if (!(flags & MS_KERNMOUNT) &&
+ if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT)) &&
!(type->fs_flags & FS_USERNS_MOUNT) &&
!capable(CAP_SYS_ADMIN))
return ERR_PTR(-EPERM);
}
if (!s) {
spin_unlock(&sb_lock);
- s = alloc_super(type, flags, user_ns);
+ s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
if (!s)
return ERR_PTR(-ENOMEM);
goto retry;
{
struct user_namespace *user_ns = current_user_ns();
+ /* We don't yet pass the user namespace of the parent
+ * mount through to here so always use &init_user_ns
+ * until that changes.
+ */
+ if (flags & MS_SUBMOUNT)
+ user_ns = &init_user_ns;
+
/* Ensure the requestor has permissions over the target filesystem */
- if (!(flags & MS_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN))
+ if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT)) && !ns_capable(user_ns, CAP_SYS_ADMIN))
return ERR_PTR(-EPERM);
return sget_userns(type, test, set, flags, user_ns, data);
if (IS_ERR(bdev))
return ERR_CAST(bdev);
+ if (current_user_ns() != &init_user_ns) {
+ /*
+ * For userns mounts, disallow mounting if bdev is open for
+ * writing
+ */
+ if (!atomic_dec_unless_positive(&bdev->bd_inode->i_writecount)) {
+ error = -EBUSY;
+ goto error_bdev;
+ }
+ if (bdev->bd_contains != bdev &&
+ !atomic_dec_unless_positive(&bdev->bd_contains->bd_inode->i_writecount)) {
+ atomic_inc(&bdev->bd_inode->i_writecount);
+ error = -EBUSY;
+ goto error_bdev;
+ }
+ }
+
/*
* once the super is inserted into the list by sget, s_umount
* will protect the lockfs code from trying to start a snapshot
if (bdev->bd_fsfreeze_count > 0) {
mutex_unlock(&bdev->bd_fsfreeze_mutex);
error = -EBUSY;
- goto error_bdev;
+ goto error_inc;
}
s = sget(fs_type, test_bdev_super, set_bdev_super, flags | MS_NOSEC,
bdev);
if ((flags ^ s->s_flags) & MS_RDONLY) {
deactivate_locked_super(s);
error = -EBUSY;
- goto error_bdev;
+ goto error_inc;
}
/*
error_s:
error = PTR_ERR(s);
+error_inc:
+ if (current_user_ns() != &init_user_ns) {
+ atomic_inc(&bdev->bd_inode->i_writecount);
+ if (bdev->bd_contains != bdev)
+ atomic_inc(&bdev->bd_contains->bd_inode->i_writecount);
+ }
error_bdev:
blkdev_put(bdev, mode);
error:
generic_shutdown_super(sb);
sync_blockdev(bdev);
WARN_ON_ONCE(!(mode & FMODE_EXCL));
+ if (sb->s_user_ns != &init_user_ns) {
+ atomic_inc(&bdev->bd_inode->i_writecount);
+ if (bdev->bd_contains != bdev)
+ atomic_inc(&bdev->bd_contains->bd_inode->i_writecount);
+ }
blkdev_put(bdev, mode | FMODE_EXCL);
}