vfs: Create fs_context-aware mount_bdev() replacement
Create a function, get_tree_bdev(), that is fs_context-aware and a ->get_tree() counterpart of mount_bdev(). It caches the block device pointer in the fs_context struct so that this information can be passed into sget_fc()'s test and set functions. Signed-off-by: David Howells <dhowells@redhat.com> cc: Jens Axboe <axboe@kernel.dk> cc: linux-block@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Родитель
533770cc0a
Коммит
fe62c3a4e1
94
fs/super.c
94
fs/super.c
|
@ -1222,6 +1222,7 @@ int get_tree_keyed(struct fs_context *fc,
|
|||
EXPORT_SYMBOL(get_tree_keyed);
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
|
||||
static int set_bdev_super(struct super_block *s, void *data)
|
||||
{
|
||||
s->s_bdev = data;
|
||||
|
@ -1231,6 +1232,99 @@ static int set_bdev_super(struct super_block *s, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int set_bdev_super_fc(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
return set_bdev_super(s, fc->sget_key);
|
||||
}
|
||||
|
||||
static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
return s->s_bdev == fc->sget_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_tree_bdev - Get a superblock based on a single block device
|
||||
* @fc: The filesystem context holding the parameters
|
||||
* @fill_super: Helper to initialise a new superblock
|
||||
*/
|
||||
int get_tree_bdev(struct fs_context *fc,
|
||||
int (*fill_super)(struct super_block *,
|
||||
struct fs_context *))
|
||||
{
|
||||
struct block_device *bdev;
|
||||
struct super_block *s;
|
||||
fmode_t mode = FMODE_READ | FMODE_EXCL;
|
||||
int error = 0;
|
||||
|
||||
if (!(fc->sb_flags & SB_RDONLY))
|
||||
mode |= FMODE_WRITE;
|
||||
|
||||
if (!fc->source)
|
||||
return invalf(fc, "No source specified");
|
||||
|
||||
bdev = blkdev_get_by_path(fc->source, mode, fc->fs_type);
|
||||
if (IS_ERR(bdev)) {
|
||||
errorf(fc, "%s: Can't open blockdev", fc->source);
|
||||
return PTR_ERR(bdev);
|
||||
}
|
||||
|
||||
/* Once the superblock is inserted into the list by sget_fc(), s_umount
|
||||
* will protect the lockfs code from trying to start a snapshot while
|
||||
* we are mounting
|
||||
*/
|
||||
mutex_lock(&bdev->bd_fsfreeze_mutex);
|
||||
if (bdev->bd_fsfreeze_count > 0) {
|
||||
mutex_unlock(&bdev->bd_fsfreeze_mutex);
|
||||
warnf(fc, "%pg: Can't mount, blockdev is frozen", bdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
fc->sb_flags |= SB_NOSEC;
|
||||
fc->sget_key = bdev;
|
||||
s = sget_fc(fc, test_bdev_super_fc, set_bdev_super_fc);
|
||||
mutex_unlock(&bdev->bd_fsfreeze_mutex);
|
||||
if (IS_ERR(s))
|
||||
return PTR_ERR(s);
|
||||
|
||||
if (s->s_root) {
|
||||
/* Don't summarily change the RO/RW state. */
|
||||
if ((fc->sb_flags ^ s->s_flags) & SB_RDONLY) {
|
||||
warnf(fc, "%pg: Can't mount, would change RO state", bdev);
|
||||
deactivate_locked_super(s);
|
||||
blkdev_put(bdev, mode);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* s_umount nests inside bd_mutex during
|
||||
* __invalidate_device(). blkdev_put() acquires
|
||||
* bd_mutex and can't be called under s_umount. Drop
|
||||
* s_umount temporarily. This is safe as we're
|
||||
* holding an active reference.
|
||||
*/
|
||||
up_write(&s->s_umount);
|
||||
blkdev_put(bdev, mode);
|
||||
down_write(&s->s_umount);
|
||||
} else {
|
||||
s->s_mode = mode;
|
||||
snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
|
||||
sb_set_blocksize(s, block_size(bdev));
|
||||
error = fill_super(s, fc);
|
||||
if (error) {
|
||||
deactivate_locked_super(s);
|
||||
return error;
|
||||
}
|
||||
|
||||
s->s_flags |= SB_ACTIVE;
|
||||
bdev->bd_super = s;
|
||||
}
|
||||
|
||||
BUG_ON(fc->root);
|
||||
fc->root = dget(s->s_root);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(get_tree_bdev);
|
||||
|
||||
static int test_bdev_super(struct super_block *s, void *data)
|
||||
{
|
||||
return (void *)s->s_bdev == data;
|
||||
|
|
|
@ -88,6 +88,7 @@ struct fs_context {
|
|||
struct mutex uapi_mutex; /* Userspace access mutex */
|
||||
struct file_system_type *fs_type;
|
||||
void *fs_private; /* The filesystem's context */
|
||||
void *sget_key;
|
||||
struct dentry *root; /* The root and superblock */
|
||||
struct user_namespace *user_ns; /* The user namespace for this mount */
|
||||
struct net *net_ns; /* The network namespace for this mount */
|
||||
|
@ -159,6 +160,10 @@ extern int get_tree_keyed(struct fs_context *fc,
|
|||
struct fs_context *fc),
|
||||
void *key);
|
||||
|
||||
extern int get_tree_bdev(struct fs_context *fc,
|
||||
int (*fill_super)(struct super_block *sb,
|
||||
struct fs_context *fc));
|
||||
|
||||
extern const struct file_operations fscontext_fops;
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче