^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* MTD-based superblock management
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Written by: David Howells <dhowells@redhat.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * David Woodhouse <dwmw2@infradead.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/mtd/super.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/namei.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/export.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/ctype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/major.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/backing-dev.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/fs_context.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include "mtdcore.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * compare superblocks to see if they're equivalent
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * - they are if the underlying MTD device is the same
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static int mtd_test_super(struct super_block *sb, struct fs_context *fc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) struct mtd_info *mtd = fc->sget_key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) if (sb->s_mtd == fc->sget_key) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) pr_debug("MTDSB: Match on device %d (\"%s\")\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) mtd->index, mtd->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * mark the superblock by the MTD device it is using
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * - set the device number to be the correct MTD block device for pesuperstence
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * of NFS exports
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static int mtd_set_super(struct super_block *sb, struct fs_context *fc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) sb->s_mtd = fc->sget_key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) sb->s_bdi = bdi_get(mtd_bdi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * get a superblock on an MTD-backed filesystem
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) static int mtd_get_sb(struct fs_context *fc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) struct mtd_info *mtd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) int (*fill_super)(struct super_block *,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) struct fs_context *))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct super_block *sb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) fc->sget_key = mtd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) sb = sget_fc(fc, mtd_test_super, mtd_set_super);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (IS_ERR(sb))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) return PTR_ERR(sb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (sb->s_root) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /* new mountpoint for an already mounted superblock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) mtd->index, mtd->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) put_mtd_device(mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) /* fresh new superblock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) mtd->index, mtd->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) ret = fill_super(sb, fc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) goto error_sb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) sb->s_flags |= SB_ACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) BUG_ON(fc->root);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) fc->root = dget(sb->s_root);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) error_sb:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) deactivate_locked_super(sb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) * get a superblock on an MTD-backed filesystem by MTD device number
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static int mtd_get_sb_by_nr(struct fs_context *fc, int mtdnr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) int (*fill_super)(struct super_block *,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) struct fs_context *))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) struct mtd_info *mtd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) mtd = get_mtd_device(NULL, mtdnr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (IS_ERR(mtd)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) errorf(fc, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return PTR_ERR(mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return mtd_get_sb(fc, mtd, fill_super);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) * get_tree_mtd - Get a superblock based on a single MTD device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) * @fc: The filesystem context holding the parameters
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) * @fill_super: Helper to initialise a new superblock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) int get_tree_mtd(struct fs_context *fc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) int (*fill_super)(struct super_block *sb,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) struct fs_context *fc))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) #ifdef CONFIG_BLOCK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) struct block_device *bdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) int ret, major;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) int mtdnr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) if (!fc->source)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) return invalf(fc, "No source specified");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) pr_debug("MTDSB: dev_name \"%s\"\n", fc->source);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) /* the preferred way of mounting in future; especially when
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) * by name, so that we don't require block device support to be present
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) * in the kernel.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) if (fc->source[0] == 'm' &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) fc->source[1] == 't' &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) fc->source[2] == 'd') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) if (fc->source[3] == ':') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) struct mtd_info *mtd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) /* mount by MTD device name */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) fc->source + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) mtd = get_mtd_device_nm(fc->source + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if (!IS_ERR(mtd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) return mtd_get_sb(fc, mtd, fill_super);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) errorf(fc, "MTD: MTD device with name \"%s\" not found",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) fc->source + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) } else if (isdigit(fc->source[3])) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) /* mount by MTD device number name */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) char *endptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) mtdnr = simple_strtoul(fc->source + 3, &endptr, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) if (!*endptr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) /* It was a valid number */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) #ifdef CONFIG_BLOCK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) /* try the old way - the hack where we allowed users to mount
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) bdev = lookup_bdev(fc->source);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) if (IS_ERR(bdev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) ret = PTR_ERR(bdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) pr_debug("MTDSB: lookup_bdev() returned 0\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) major = MAJOR(bdev->bd_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) mtdnr = MINOR(bdev->bd_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) bdput(bdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (major == MTD_BLOCK_MAJOR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) #endif /* CONFIG_BLOCK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) if (!(fc->sb_flags & SB_SILENT))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) errorf(fc, "MTD: Attempt to mount non-MTD device \"%s\"",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) fc->source);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) EXPORT_SYMBOL_GPL(get_tree_mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) * destroy an MTD-based superblock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) void kill_mtd_super(struct super_block *sb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) generic_shutdown_super(sb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) put_mtd_device(sb->s_mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) sb->s_mtd = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) EXPORT_SYMBOL_GPL(kill_mtd_super);