linux/drivers/mtd/mtdsuper.c
<<
>>
Prefs
   1/* MTD-based superblock management
   2 *
   3 * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved.
   4 * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>
   5 *
   6 * Written by:  David Howells <dhowells@redhat.com>
   7 *              David Woodhouse <dwmw2@infradead.org>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License
  11 * as published by the Free Software Foundation; either version
  12 * 2 of the License, or (at your option) any later version.
  13 */
  14
  15#include <linux/mtd/super.h>
  16#include <linux/namei.h>
  17#include <linux/export.h>
  18#include <linux/ctype.h>
  19#include <linux/slab.h>
  20
  21/*
  22 * compare superblocks to see if they're equivalent
  23 * - they are if the underlying MTD device is the same
  24 */
  25static int get_sb_mtd_compare(struct super_block *sb, void *_mtd)
  26{
  27        struct mtd_info *mtd = _mtd;
  28
  29        if (sb->s_mtd == mtd) {
  30                pr_debug("MTDSB: Match on device %d (\"%s\")\n",
  31                      mtd->index, mtd->name);
  32                return 1;
  33        }
  34
  35        pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
  36              sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
  37        return 0;
  38}
  39
  40/*
  41 * mark the superblock by the MTD device it is using
  42 * - set the device number to be the correct MTD block device for pesuperstence
  43 *   of NFS exports
  44 */
  45static int get_sb_mtd_set(struct super_block *sb, void *_mtd)
  46{
  47        struct mtd_info *mtd = _mtd;
  48
  49        sb->s_mtd = mtd;
  50        sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
  51        sb->s_bdi = mtd->backing_dev_info;
  52        return 0;
  53}
  54
  55/*
  56 * get a superblock on an MTD-backed filesystem
  57 */
  58static struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags,
  59                          const char *dev_name, void *data,
  60                          struct mtd_info *mtd,
  61                          int (*fill_super)(struct super_block *, void *, int))
  62{
  63        struct super_block *sb;
  64        int ret;
  65
  66        sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, flags, mtd);
  67        if (IS_ERR(sb))
  68                goto out_error;
  69
  70        if (sb->s_root)
  71                goto already_mounted;
  72
  73        /* fresh new superblock */
  74        pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
  75              mtd->index, mtd->name);
  76
  77        ret = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
  78        if (ret < 0) {
  79                deactivate_locked_super(sb);
  80                return ERR_PTR(ret);
  81        }
  82
  83        /* go */
  84        sb->s_flags |= MS_ACTIVE;
  85        return dget(sb->s_root);
  86
  87        /* new mountpoint for an already mounted superblock */
  88already_mounted:
  89        pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
  90              mtd->index, mtd->name);
  91        put_mtd_device(mtd);
  92        return dget(sb->s_root);
  93
  94out_error:
  95        put_mtd_device(mtd);
  96        return ERR_CAST(sb);
  97}
  98
  99/*
 100 * get a superblock on an MTD-backed filesystem by MTD device number
 101 */
 102static struct dentry *mount_mtd_nr(struct file_system_type *fs_type, int flags,
 103                         const char *dev_name, void *data, int mtdnr,
 104                         int (*fill_super)(struct super_block *, void *, int))
 105{
 106        struct mtd_info *mtd;
 107
 108        mtd = get_mtd_device(NULL, mtdnr);
 109        if (IS_ERR(mtd)) {
 110                pr_debug("MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
 111                return ERR_CAST(mtd);
 112        }
 113
 114        return mount_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super);
 115}
 116
 117/*
 118 * set up an MTD-based superblock
 119 */
 120struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
 121               const char *dev_name, void *data,
 122               int (*fill_super)(struct super_block *, void *, int))
 123{
 124#ifdef CONFIG_BLOCK
 125        struct block_device *bdev;
 126        int ret, major;
 127#endif
 128        int mtdnr;
 129
 130        if (!dev_name)
 131                return ERR_PTR(-EINVAL);
 132
 133        pr_debug("MTDSB: dev_name \"%s\"\n", dev_name);
 134
 135        /* the preferred way of mounting in future; especially when
 136         * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
 137         * by name, so that we don't require block device support to be present
 138         * in the kernel. */
 139        if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
 140                if (dev_name[3] == ':') {
 141                        struct mtd_info *mtd;
 142
 143                        /* mount by MTD device name */
 144                        pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
 145                              dev_name + 4);
 146
 147                        mtd = get_mtd_device_nm(dev_name + 4);
 148                        if (!IS_ERR(mtd))
 149                                return mount_mtd_aux(
 150                                        fs_type, flags,
 151                                        dev_name, data, mtd,
 152                                        fill_super);
 153
 154                        printk(KERN_NOTICE "MTD:"
 155                               " MTD device with name \"%s\" not found.\n",
 156                               dev_name + 4);
 157
 158                } else if (isdigit(dev_name[3])) {
 159                        /* mount by MTD device number name */
 160                        char *endptr;
 161
 162                        mtdnr = simple_strtoul(dev_name + 3, &endptr, 0);
 163                        if (!*endptr) {
 164                                /* It was a valid number */
 165                                pr_debug("MTDSB: mtd%%d, mtdnr %d\n",
 166                                      mtdnr);
 167                                return mount_mtd_nr(fs_type, flags,
 168                                                     dev_name, data,
 169                                                     mtdnr, fill_super);
 170                        }
 171                }
 172        }
 173
 174#ifdef CONFIG_BLOCK
 175        /* try the old way - the hack where we allowed users to mount
 176         * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
 177         */
 178        bdev = lookup_bdev(dev_name);
 179        if (IS_ERR(bdev)) {
 180                ret = PTR_ERR(bdev);
 181                pr_debug("MTDSB: lookup_bdev() returned %d\n", ret);
 182                return ERR_PTR(ret);
 183        }
 184        pr_debug("MTDSB: lookup_bdev() returned 0\n");
 185
 186        ret = -EINVAL;
 187
 188        major = MAJOR(bdev->bd_dev);
 189        mtdnr = MINOR(bdev->bd_dev);
 190        bdput(bdev);
 191
 192        if (major != MTD_BLOCK_MAJOR)
 193                goto not_an_MTD_device;
 194
 195        return mount_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super);
 196
 197not_an_MTD_device:
 198#endif /* CONFIG_BLOCK */
 199
 200        if (!(flags & MS_SILENT))
 201                printk(KERN_NOTICE
 202                       "MTD: Attempt to mount non-MTD device \"%s\"\n",
 203                       dev_name);
 204        return ERR_PTR(-EINVAL);
 205}
 206
 207EXPORT_SYMBOL_GPL(mount_mtd);
 208
 209/*
 210 * destroy an MTD-based superblock
 211 */
 212void kill_mtd_super(struct super_block *sb)
 213{
 214        generic_shutdown_super(sb);
 215        put_mtd_device(sb->s_mtd);
 216        sb->s_mtd = NULL;
 217}
 218
 219EXPORT_SYMBOL_GPL(kill_mtd_super);
 220