Blame view

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