Merge tag 'v6.6-vfs.super.fixes.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull more superblock follow-on fixes from Christian Brauner:
"This contains two more small follow-up fixes for the super work this
cycle. I went through all filesystems once more and detected two minor
issues that still needed fixing:
- Some filesystems support mtd devices (e.g., mount -t jffs2 mtd2
/mnt). The mtd infrastructure uses the sb->s_mtd pointer to find an
existing superblock. When the mtd device is put and sb->s_mtd
cleared the superblock can still be found fs_supers and so this
risks a use-after-free.
Add a small patch that aligns mtd with what we did for regular
block devices and switch keying to rely on sb->s_dev.
(This was tested with mtd devices and jffs2 as xfstests doesn't
support mtd devices.)
- Switch nfs back to rely on kill_anon_super() so the superblock is
removed from the list of active supers before sb->s_fs_info is
freed"
* tag 'v6.6-vfs.super.fixes.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
NFS: switch back to using kill_anon_super
mtd: key superblock by device number
fs: export sget_dev()
This commit is contained in:
+11
-34
@@ -19,38 +19,6 @@
|
|||||||
#include <linux/fs_context.h>
|
#include <linux/fs_context.h>
|
||||||
#include "mtdcore.h"
|
#include "mtdcore.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* compare superblocks to see if they're equivalent
|
|
||||||
* - they are if the underlying MTD device is the same
|
|
||||||
*/
|
|
||||||
static int mtd_test_super(struct super_block *sb, struct fs_context *fc)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = fc->sget_key;
|
|
||||||
|
|
||||||
if (sb->s_mtd == fc->sget_key) {
|
|
||||||
pr_debug("MTDSB: Match on device %d (\"%s\")\n",
|
|
||||||
mtd->index, mtd->name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
|
|
||||||
sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* mark the superblock by the MTD device it is using
|
|
||||||
* - set the device number to be the correct MTD block device for pesuperstence
|
|
||||||
* of NFS exports
|
|
||||||
*/
|
|
||||||
static int mtd_set_super(struct super_block *sb, struct fs_context *fc)
|
|
||||||
{
|
|
||||||
sb->s_mtd = fc->sget_key;
|
|
||||||
sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index);
|
|
||||||
sb->s_bdi = bdi_get(mtd_bdi);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get a superblock on an MTD-backed filesystem
|
* get a superblock on an MTD-backed filesystem
|
||||||
*/
|
*/
|
||||||
@@ -62,8 +30,7 @@ static int mtd_get_sb(struct fs_context *fc,
|
|||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
fc->sget_key = mtd;
|
sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index));
|
||||||
sb = sget_fc(fc, mtd_test_super, mtd_set_super);
|
|
||||||
if (IS_ERR(sb))
|
if (IS_ERR(sb))
|
||||||
return PTR_ERR(sb);
|
return PTR_ERR(sb);
|
||||||
|
|
||||||
@@ -77,6 +44,16 @@ static int mtd_get_sb(struct fs_context *fc,
|
|||||||
pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
|
pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
|
||||||
mtd->index, mtd->name);
|
mtd->index, mtd->name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Would usually have been set with @sb_lock held but in
|
||||||
|
* contrast to sb->s_bdev that's checked with only
|
||||||
|
* @sb_lock held, nothing checks sb->s_mtd without also
|
||||||
|
* holding sb->s_umount and we're holding sb->s_umount
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
sb->s_mtd = mtd;
|
||||||
|
sb->s_bdi = bdi_get(mtd_bdi);
|
||||||
|
|
||||||
ret = fill_super(sb, fc);
|
ret = fill_super(sb, fc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error_sb;
|
goto error_sb;
|
||||||
|
|||||||
+1
-3
@@ -1339,15 +1339,13 @@ error_splat_super:
|
|||||||
void nfs_kill_super(struct super_block *s)
|
void nfs_kill_super(struct super_block *s)
|
||||||
{
|
{
|
||||||
struct nfs_server *server = NFS_SB(s);
|
struct nfs_server *server = NFS_SB(s);
|
||||||
dev_t dev = s->s_dev;
|
|
||||||
|
|
||||||
nfs_sysfs_move_sb_to_server(server);
|
nfs_sysfs_move_sb_to_server(server);
|
||||||
generic_shutdown_super(s);
|
kill_anon_super(s);
|
||||||
|
|
||||||
nfs_fscache_release_super_cookie(s);
|
nfs_fscache_release_super_cookie(s);
|
||||||
|
|
||||||
nfs_free_server(server);
|
nfs_free_server(server);
|
||||||
free_anon_bdev(dev);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nfs_kill_super);
|
EXPORT_SYMBOL_GPL(nfs_kill_super);
|
||||||
|
|
||||||
|
|||||||
+45
-19
@@ -1373,6 +1373,50 @@ int get_tree_keyed(struct fs_context *fc,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(get_tree_keyed);
|
EXPORT_SYMBOL(get_tree_keyed);
|
||||||
|
|
||||||
|
static int set_bdev_super(struct super_block *s, void *data)
|
||||||
|
{
|
||||||
|
s->s_dev = *(dev_t *)data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int super_s_dev_set(struct super_block *s, struct fs_context *fc)
|
||||||
|
{
|
||||||
|
return set_bdev_super(s, fc->sget_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int super_s_dev_test(struct super_block *s, struct fs_context *fc)
|
||||||
|
{
|
||||||
|
return !(s->s_iflags & SB_I_RETIRED) &&
|
||||||
|
s->s_dev == *(dev_t *)fc->sget_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sget_dev - Find or create a superblock by device number
|
||||||
|
* @fc: Filesystem context.
|
||||||
|
* @dev: device number
|
||||||
|
*
|
||||||
|
* Find or create a superblock using the provided device number that
|
||||||
|
* will be stored in fc->sget_key.
|
||||||
|
*
|
||||||
|
* If an extant superblock is matched, then that will be returned with
|
||||||
|
* an elevated reference count that the caller must transfer or discard.
|
||||||
|
*
|
||||||
|
* If no match is made, a new superblock will be allocated and basic
|
||||||
|
* initialisation will be performed (s_type, s_fs_info, s_id, s_dev will
|
||||||
|
* be set). The superblock will be published and it will be returned in
|
||||||
|
* a partially constructed state with SB_BORN and SB_ACTIVE as yet
|
||||||
|
* unset.
|
||||||
|
*
|
||||||
|
* Return: an existing or newly created superblock on success, an error
|
||||||
|
* pointer on failure.
|
||||||
|
*/
|
||||||
|
struct super_block *sget_dev(struct fs_context *fc, dev_t dev)
|
||||||
|
{
|
||||||
|
fc->sget_key = &dev;
|
||||||
|
return sget_fc(fc, super_s_dev_test, super_s_dev_set);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sget_dev);
|
||||||
|
|
||||||
#ifdef CONFIG_BLOCK
|
#ifdef CONFIG_BLOCK
|
||||||
/*
|
/*
|
||||||
* Lock a super block that the callers holds a reference to.
|
* Lock a super block that the callers holds a reference to.
|
||||||
@@ -1431,23 +1475,6 @@ const struct blk_holder_ops fs_holder_ops = {
|
|||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(fs_holder_ops);
|
EXPORT_SYMBOL_GPL(fs_holder_ops);
|
||||||
|
|
||||||
static int set_bdev_super(struct super_block *s, void *data)
|
|
||||||
{
|
|
||||||
s->s_dev = *(dev_t *)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_iflags & SB_I_RETIRED) &&
|
|
||||||
s->s_dev == *(dev_t *)fc->sget_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setup_bdev_super(struct super_block *sb, int sb_flags,
|
int setup_bdev_super(struct super_block *sb, int sb_flags,
|
||||||
struct fs_context *fc)
|
struct fs_context *fc)
|
||||||
{
|
{
|
||||||
@@ -1525,8 +1552,7 @@ int get_tree_bdev(struct fs_context *fc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fc->sb_flags |= SB_NOSEC;
|
fc->sb_flags |= SB_NOSEC;
|
||||||
fc->sget_key = &dev;
|
s = sget_dev(fc, dev);
|
||||||
s = sget_fc(fc, test_bdev_super_fc, set_bdev_super_fc);
|
|
||||||
if (IS_ERR(s))
|
if (IS_ERR(s))
|
||||||
return PTR_ERR(s);
|
return PTR_ERR(s);
|
||||||
|
|
||||||
|
|||||||
@@ -2397,6 +2397,7 @@ struct super_block *sget(struct file_system_type *type,
|
|||||||
int (*test)(struct super_block *,void *),
|
int (*test)(struct super_block *,void *),
|
||||||
int (*set)(struct super_block *,void *),
|
int (*set)(struct super_block *,void *),
|
||||||
int flags, void *data);
|
int flags, void *data);
|
||||||
|
struct super_block *sget_dev(struct fs_context *fc, dev_t dev);
|
||||||
|
|
||||||
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
|
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
|
||||||
#define fops_get(fops) \
|
#define fops_get(fops) \
|
||||||
|
|||||||
Reference in New Issue
Block a user