Commit e7e9423d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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()
parents f441ff73 5069ba84
Loading
Loading
Loading
Loading
+11 −34
Original line number Diff line number Diff line
@@ -19,38 +19,6 @@
#include <linux/fs_context.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
 */
@@ -62,8 +30,7 @@ static int mtd_get_sb(struct fs_context *fc,
	struct super_block *sb;
	int ret;

	fc->sget_key = mtd;
	sb = sget_fc(fc, mtd_test_super, mtd_set_super);
	sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index));
	if (IS_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",
			 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);
		if (ret < 0)
			goto error_sb;
+1 −3
Original line number Diff line number Diff line
@@ -1339,15 +1339,13 @@ int nfs_get_tree_common(struct fs_context *fc)
void nfs_kill_super(struct super_block *s)
{
	struct nfs_server *server = NFS_SB(s);
	dev_t dev = s->s_dev;

	nfs_sysfs_move_sb_to_server(server);
	generic_shutdown_super(s);
	kill_anon_super(s);

	nfs_fscache_release_super_cookie(s);

	nfs_free_server(server);
	free_anon_bdev(dev);
}
EXPORT_SYMBOL_GPL(nfs_kill_super);

+45 −19
Original line number Diff line number Diff line
@@ -1373,6 +1373,50 @@ int get_tree_keyed(struct fs_context *fc,
}
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
/*
 * 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);

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,
		struct fs_context *fc)
{
@@ -1525,8 +1552,7 @@ int get_tree_bdev(struct fs_context *fc,
	}

	fc->sb_flags |= SB_NOSEC;
	fc->sget_key = &dev;
	s = sget_fc(fc, test_bdev_super_fc, set_bdev_super_fc);
	s = sget_dev(fc, dev);
	if (IS_ERR(s))
		return PTR_ERR(s);

+1 −0
Original line number Diff line number Diff line
@@ -2397,6 +2397,7 @@ struct super_block *sget(struct file_system_type *type,
			int (*test)(struct super_block *,void *),
			int (*set)(struct super_block *,void *),
			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 */
#define fops_get(fops) \