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