Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* MTD-based superblock management
0003  *
0004  * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved.
0005  * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>
0006  *
0007  * Written by:  David Howells <dhowells@redhat.com>
0008  *              David Woodhouse <dwmw2@infradead.org>
0009  */
0010 
0011 #include <linux/mtd/super.h>
0012 #include <linux/namei.h>
0013 #include <linux/export.h>
0014 #include <linux/ctype.h>
0015 #include <linux/slab.h>
0016 #include <linux/major.h>
0017 #include <linux/backing-dev.h>
0018 #include <linux/blkdev.h>
0019 #include <linux/fs_context.h>
0020 #include "mtdcore.h"
0021 
0022 /*
0023  * compare superblocks to see if they're equivalent
0024  * - they are if the underlying MTD device is the same
0025  */
0026 static int mtd_test_super(struct super_block *sb, struct fs_context *fc)
0027 {
0028     struct mtd_info *mtd = fc->sget_key;
0029 
0030     if (sb->s_mtd == fc->sget_key) {
0031         pr_debug("MTDSB: Match on device %d (\"%s\")\n",
0032              mtd->index, mtd->name);
0033         return 1;
0034     }
0035 
0036     pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
0037          sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
0038     return 0;
0039 }
0040 
0041 /*
0042  * mark the superblock by the MTD device it is using
0043  * - set the device number to be the correct MTD block device for pesuperstence
0044  *   of NFS exports
0045  */
0046 static int mtd_set_super(struct super_block *sb, struct fs_context *fc)
0047 {
0048     sb->s_mtd = fc->sget_key;
0049     sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index);
0050     sb->s_bdi = bdi_get(mtd_bdi);
0051     return 0;
0052 }
0053 
0054 /*
0055  * get a superblock on an MTD-backed filesystem
0056  */
0057 static int mtd_get_sb(struct fs_context *fc,
0058               struct mtd_info *mtd,
0059               int (*fill_super)(struct super_block *,
0060                     struct fs_context *))
0061 {
0062     struct super_block *sb;
0063     int ret;
0064 
0065     fc->sget_key = mtd;
0066     sb = sget_fc(fc, mtd_test_super, mtd_set_super);
0067     if (IS_ERR(sb))
0068         return PTR_ERR(sb);
0069 
0070     if (sb->s_root) {
0071         /* new mountpoint for an already mounted superblock */
0072         pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
0073              mtd->index, mtd->name);
0074         put_mtd_device(mtd);
0075     } else {
0076         /* fresh new superblock */
0077         pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
0078              mtd->index, mtd->name);
0079 
0080         ret = fill_super(sb, fc);
0081         if (ret < 0)
0082             goto error_sb;
0083 
0084         sb->s_flags |= SB_ACTIVE;
0085     }
0086 
0087     BUG_ON(fc->root);
0088     fc->root = dget(sb->s_root);
0089     return 0;
0090 
0091 error_sb:
0092     deactivate_locked_super(sb);
0093     return ret;
0094 }
0095 
0096 /*
0097  * get a superblock on an MTD-backed filesystem by MTD device number
0098  */
0099 static int mtd_get_sb_by_nr(struct fs_context *fc, int mtdnr,
0100                 int (*fill_super)(struct super_block *,
0101                           struct fs_context *))
0102 {
0103     struct mtd_info *mtd;
0104 
0105     mtd = get_mtd_device(NULL, mtdnr);
0106     if (IS_ERR(mtd)) {
0107         errorf(fc, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
0108         return PTR_ERR(mtd);
0109     }
0110 
0111     return mtd_get_sb(fc, mtd, fill_super);
0112 }
0113 
0114 /**
0115  * get_tree_mtd - Get a superblock based on a single MTD device
0116  * @fc: The filesystem context holding the parameters
0117  * @fill_super: Helper to initialise a new superblock
0118  */
0119 int get_tree_mtd(struct fs_context *fc,
0120           int (*fill_super)(struct super_block *sb,
0121                 struct fs_context *fc))
0122 {
0123 #ifdef CONFIG_BLOCK
0124     dev_t dev;
0125     int ret;
0126 #endif
0127     int mtdnr;
0128 
0129     if (!fc->source)
0130         return invalf(fc, "No source specified");
0131 
0132     pr_debug("MTDSB: dev_name \"%s\"\n", fc->source);
0133 
0134     /* the preferred way of mounting in future; especially when
0135      * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
0136      * by name, so that we don't require block device support to be present
0137      * in the kernel.
0138      */
0139     if (fc->source[0] == 'm' &&
0140         fc->source[1] == 't' &&
0141         fc->source[2] == 'd') {
0142         if (fc->source[3] == ':') {
0143             struct mtd_info *mtd;
0144 
0145             /* mount by MTD device name */
0146             pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
0147                  fc->source + 4);
0148 
0149             mtd = get_mtd_device_nm(fc->source + 4);
0150             if (!IS_ERR(mtd))
0151                 return mtd_get_sb(fc, mtd, fill_super);
0152 
0153             errorf(fc, "MTD: MTD device with name \"%s\" not found",
0154                    fc->source + 4);
0155 
0156         } else if (isdigit(fc->source[3])) {
0157             /* mount by MTD device number name */
0158             char *endptr;
0159 
0160             mtdnr = simple_strtoul(fc->source + 3, &endptr, 0);
0161             if (!*endptr) {
0162                 /* It was a valid number */
0163                 pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr);
0164                 return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
0165             }
0166         }
0167     }
0168 
0169 #ifdef CONFIG_BLOCK
0170     /* try the old way - the hack where we allowed users to mount
0171      * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
0172      */
0173     ret = lookup_bdev(fc->source, &dev);
0174     if (ret) {
0175         errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret);
0176         return ret;
0177     }
0178     pr_debug("MTDSB: lookup_bdev() returned 0\n");
0179 
0180     if (MAJOR(dev) == MTD_BLOCK_MAJOR)
0181         return mtd_get_sb_by_nr(fc, MINOR(dev), fill_super);
0182 
0183 #endif /* CONFIG_BLOCK */
0184 
0185     if (!(fc->sb_flags & SB_SILENT))
0186         errorf(fc, "MTD: Attempt to mount non-MTD device \"%s\"",
0187                fc->source);
0188     return -EINVAL;
0189 }
0190 EXPORT_SYMBOL_GPL(get_tree_mtd);
0191 
0192 /*
0193  * destroy an MTD-based superblock
0194  */
0195 void kill_mtd_super(struct super_block *sb)
0196 {
0197     generic_shutdown_super(sb);
0198     put_mtd_device(sb->s_mtd);
0199     sb->s_mtd = NULL;
0200 }
0201 
0202 EXPORT_SYMBOL_GPL(kill_mtd_super);