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