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