1acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* MTD-based superblock management 2acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * 3acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved. 4a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> 5a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * 6acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * Written by: David Howells <dhowells@redhat.com> 7acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * David Woodhouse <dwmw2@infradead.org> 8acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * 9acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * This program is free software; you can redistribute it and/or 10acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * modify it under the terms of the GNU General Public License 11acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * as published by the Free Software Foundation; either version 12acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * 2 of the License, or (at your option) any later version. 13acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 14acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 15acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells#include <linux/mtd/super.h> 16acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells#include <linux/namei.h> 17f3bcc0179ab8145615a3b409d652cad1395fb7f3Paul Gortmaker#include <linux/export.h> 18acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells#include <linux/ctype.h> 196de9400250f95f82da432c28b9b43823f4154c58Jörn Engel#include <linux/slab.h> 20acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 21acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* 22acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * compare superblocks to see if they're equivalent 23acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * - they are if the underlying MTD device is the same 24acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 25acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howellsstatic int get_sb_mtd_compare(struct super_block *sb, void *_mtd) 26acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells{ 27acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells struct mtd_info *mtd = _mtd; 28acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 29acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (sb->s_mtd == mtd) { 30289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: Match on device %d (\"%s\")\n", 31acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells mtd->index, mtd->name); 32acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells return 1; 33acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 34acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 35289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n", 36acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); 37acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells return 0; 38acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells} 39acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 40acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* 41acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * mark the superblock by the MTD device it is using 42acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * - set the device number to be the correct MTD block device for pesuperstence 43acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * of NFS exports 44acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 45acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howellsstatic int get_sb_mtd_set(struct super_block *sb, void *_mtd) 46acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells{ 47acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells struct mtd_info *mtd = _mtd; 48acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 49acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells sb->s_mtd = mtd; 50acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index); 516de9400250f95f82da432c28b9b43823f4154c58Jörn Engel sb->s_bdi = mtd->backing_dev_info; 52acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells return 0; 53acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells} 54acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 55acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* 56acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * get a superblock on an MTD-backed filesystem 57acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 58848b83a59b772b8f102bc5e3f1187c2fa5676959Al Virostatic struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags, 59acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells const char *dev_name, void *data, 60acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells struct mtd_info *mtd, 61848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro int (*fill_super)(struct super_block *, void *, int)) 62acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells{ 63acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells struct super_block *sb; 64acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells int ret; 65acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 66acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, mtd); 67acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (IS_ERR(sb)) 68acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells goto out_error; 69acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 70acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (sb->s_root) 71acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells goto already_mounted; 72acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 73acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* fresh new superblock */ 74289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: New superblock for device %d (\"%s\")\n", 75acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells mtd->index, mtd->name); 76acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 7748440e893d700fb8f0de95fa7d748b711d290365David Howells sb->s_flags = flags; 7848440e893d700fb8f0de95fa7d748b711d290365David Howells 79acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells ret = fill_super(sb, data, flags & MS_SILENT ? 1 : 0); 80acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (ret < 0) { 816f5bbff9a1b7d6864a495763448a363bbfa96324Al Viro deactivate_locked_super(sb); 82848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return ERR_PTR(ret); 83acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 84acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 85acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* go */ 86acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells sb->s_flags |= MS_ACTIVE; 87848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return dget(sb->s_root); 88acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 89acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* new mountpoint for an already mounted superblock */ 90acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howellsalready_mounted: 91289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n", 92acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells mtd->index, mtd->name); 93848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro put_mtd_device(mtd); 94848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return dget(sb->s_root); 95acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 96acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howellsout_error: 97acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells put_mtd_device(mtd); 98848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return ERR_CAST(sb); 99acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells} 100acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 101acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* 102acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * get a superblock on an MTD-backed filesystem by MTD device number 103acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 104848b83a59b772b8f102bc5e3f1187c2fa5676959Al Virostatic struct dentry *mount_mtd_nr(struct file_system_type *fs_type, int flags, 105acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells const char *dev_name, void *data, int mtdnr, 106848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro int (*fill_super)(struct super_block *, void *, int)) 107acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells{ 108acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells struct mtd_info *mtd; 109acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 110acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells mtd = get_mtd_device(NULL, mtdnr); 111718ea8361b15aec5f4cb559d63ba34bc5a58d8f9David Woodhouse if (IS_ERR(mtd)) { 112289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: Device #%u doesn't appear to exist\n", mtdnr); 113848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return ERR_CAST(mtd); 114acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 115acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 116848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return mount_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super); 117acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells} 118acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 119acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* 120acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * set up an MTD-based superblock 121acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 122848b83a59b772b8f102bc5e3f1187c2fa5676959Al Virostruct dentry *mount_mtd(struct file_system_type *fs_type, int flags, 123acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells const char *dev_name, void *data, 124848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro int (*fill_super)(struct super_block *, void *, int)) 125acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells{ 126f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse#ifdef CONFIG_BLOCK 127d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro struct block_device *bdev; 128f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse int ret, major; 129f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse#endif 130f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse int mtdnr; 131acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 132acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (!dev_name) 133848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return ERR_PTR(-EINVAL); 134acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 135289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: dev_name \"%s\"\n", dev_name); 136acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 137acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* the preferred way of mounting in future; especially when 138acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * CONFIG_BLOCK=n - we specify the underlying MTD device by number or 139acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * by name, so that we don't require block device support to be present 140acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * in the kernel. */ 141acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { 142acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (dev_name[3] == ':') { 143acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells struct mtd_info *mtd; 144acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 145acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* mount by MTD device name */ 146289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: mtd:%%s, name \"%s\"\n", 147acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells dev_name + 4); 148acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 149677c2aec8cdd5ae33b5fab266941cf6c6dc4d59fBen Hutchings mtd = get_mtd_device_nm(dev_name + 4); 150677c2aec8cdd5ae33b5fab266941cf6c6dc4d59fBen Hutchings if (!IS_ERR(mtd)) 151848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return mount_mtd_aux( 152677c2aec8cdd5ae33b5fab266941cf6c6dc4d59fBen Hutchings fs_type, flags, 153677c2aec8cdd5ae33b5fab266941cf6c6dc4d59fBen Hutchings dev_name, data, mtd, 154848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro fill_super); 155acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 156acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells printk(KERN_NOTICE "MTD:" 157acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells " MTD device with name \"%s\" not found.\n", 158acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells dev_name + 4); 159acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 160acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } else if (isdigit(dev_name[3])) { 161acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* mount by MTD device number name */ 162acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells char *endptr; 163acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 164acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells mtdnr = simple_strtoul(dev_name + 3, &endptr, 0); 165acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (!*endptr) { 166acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* It was a valid number */ 167289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: mtd%%d, mtdnr %d\n", 168acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells mtdnr); 169848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return mount_mtd_nr(fs_type, flags, 170acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells dev_name, data, 171848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro mtdnr, fill_super); 172acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 173acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 174acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 175acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 176f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse#ifdef CONFIG_BLOCK 177acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells /* try the old way - the hack where we allowed users to mount 178acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * /dev/mtdblock$(n) but didn't actually _use_ the blockdev 179acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 180d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro bdev = lookup_bdev(dev_name); 181d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro if (IS_ERR(bdev)) { 182d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro ret = PTR_ERR(bdev); 183289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: lookup_bdev() returned %d\n", ret); 184848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return ERR_PTR(ret); 185acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells } 186289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("MTDSB: lookup_bdev() returned 0\n"); 187acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 188d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro ret = -EINVAL; 189acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 190f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse major = MAJOR(bdev->bd_dev); 191d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro mtdnr = MINOR(bdev->bd_dev); 192d5686b444ff3f72808d2b3fbd58672a86cdf38e7Al Viro bdput(bdev); 193acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 194f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse if (major != MTD_BLOCK_MAJOR) 195f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse goto not_an_MTD_device; 196f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse 197848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return mount_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super); 198acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 199acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howellsnot_an_MTD_device: 200f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse#endif /* CONFIG_BLOCK */ 201f1136d022af8f07a97f59c6d07483bdb82ffbd8eDavid Woodhouse 202acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells if (!(flags & MS_SILENT)) 203acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells printk(KERN_NOTICE 204acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells "MTD: Attempt to mount non-MTD device \"%s\"\n", 205acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells dev_name); 206848b83a59b772b8f102bc5e3f1187c2fa5676959Al Viro return ERR_PTR(-EINVAL); 207acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells} 208acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 209848b83a59b772b8f102bc5e3f1187c2fa5676959Al ViroEXPORT_SYMBOL_GPL(mount_mtd); 210acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 211acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells/* 212acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells * destroy an MTD-based superblock 213acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells */ 214acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howellsvoid kill_mtd_super(struct super_block *sb) 215acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells{ 216acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells generic_shutdown_super(sb); 217acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells put_mtd_device(sb->s_mtd); 218acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells sb->s_mtd = NULL; 219acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells} 220acaebfd8a7af0019b2edfcf4045c56c3e18375c5David Howells 221acaebfd8a7af0019b2edfcf4045c56c3e18375c5David HowellsEXPORT_SYMBOL_GPL(kill_mtd_super); 222