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);
|
EXPORT_SYMBOL(get_tree_keyed);
|
||||||
|
|
||||||
#ifdef CONFIG_BLOCK
|
#ifdef CONFIG_BLOCK
|
||||||
|
|
||||||
static int set_bdev_super(struct super_block *s, void *data)
|
static int set_bdev_super(struct super_block *s, void *data)
|
||||||
{
|
{
|
||||||
s->s_bdev = data;
|
s->s_bdev = data;
|
||||||
|
@ -1231,6 +1232,99 @@ static int set_bdev_super(struct super_block *s, void *data)
|
||||||
return 0;
|
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)
|
static int test_bdev_super(struct super_block *s, void *data)
|
||||||
{
|
{
|
||||||
return (void *)s->s_bdev == data;
|
return (void *)s->s_bdev == data;
|
||||||
|
|
|
@ -88,6 +88,7 @@ struct fs_context {
|
||||||
struct mutex uapi_mutex; /* Userspace access mutex */
|
struct mutex uapi_mutex; /* Userspace access mutex */
|
||||||
struct file_system_type *fs_type;
|
struct file_system_type *fs_type;
|
||||||
void *fs_private; /* The filesystem's context */
|
void *fs_private; /* The filesystem's context */
|
||||||
|
void *sget_key;
|
||||||
struct dentry *root; /* The root and superblock */
|
struct dentry *root; /* The root and superblock */
|
||||||
struct user_namespace *user_ns; /* The user namespace for this mount */
|
struct user_namespace *user_ns; /* The user namespace for this mount */
|
||||||
struct net *net_ns; /* The network 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),
|
struct fs_context *fc),
|
||||||
void *key);
|
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;
|
extern const struct file_operations fscontext_fops;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче