aboutsummaryrefslogtreecommitdiffstats
path: root/fs/romfs/super.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-02-12 05:40:10 -0500
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-03-24 05:01:32 -0400
commitda4458bda237aa0cb1688f6c359477f203788f6a (patch)
tree9b76cb1ecb462cccc8412eef2af5f18dcee77b51 /fs/romfs/super.c
parent6e232cfce35a20a8702d9ac7709d35030c1b3271 (diff)
NOMMU: Make it possible for RomFS to use MTD devices directly
Change RomFS so that it can use MTD devices directly - without the intercession of the block layer - as well as using block devices. This permits RomFS: (1) to use the MTD direct mapping facility available under NOMMU conditions if the underlying device is directly accessible by the CPU (including XIP); (2) and thus to be used when the block layer is disabled. RomFS can be configured with support just for MTD devices, just for Block devices or for both. If RomFS is configured for both, then it will treat mtdblock device files as MTD backing stores, not block layer backing stores. I tested this using a CONFIG_MMU=n CONFIG_BLOCK=n kernel running on my FRV board with a RomFS image installed on the mtdram test device. I see my test program being run XIP: # cat /proc/maps ... c0c000b0-c0c01f8c r-xs 00000000 1f:00 144 /mnt/doshm ... GDB on the kernel can be used to show that these addresses are within the set-aside RAM space. Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: Bernd Schmidt <bernd.schmidt@analog.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'fs/romfs/super.c')
-rw-r--r--fs/romfs/super.c648
1 files changed, 648 insertions, 0 deletions
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
new file mode 100644
index 000000000000..1e548a4975ba
--- /dev/null
+++ b/fs/romfs/super.c
@@ -0,0 +1,648 @@
1/* Block- or MTD-based romfs
2 *
3 * Copyright © 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * Derived from: ROMFS file system, Linux implementation
7 *
8 * Copyright © 1997-1999 Janos Farkas <chexum@shadow.banki.hu>
9 *
10 * Using parts of the minix filesystem
11 * Copyright © 1991, 1992 Linus Torvalds
12 *
13 * and parts of the affs filesystem additionally
14 * Copyright © 1993 Ray Burr
15 * Copyright © 1996 Hans-Joachim Widmaier
16 *
17 * Changes
18 * Changed for 2.1.19 modules
19 * Jan 1997 Initial release
20 * Jun 1997 2.1.43+ changes
21 * Proper page locking in readpage
22 * Changed to work with 2.1.45+ fs
23 * Jul 1997 Fixed follow_link
24 * 2.1.47
25 * lookup shouldn't return -ENOENT
26 * from Horst von Brand:
27 * fail on wrong checksum
28 * double unlock_super was possible
29 * correct namelen for statfs
30 * spotted by Bill Hawes:
31 * readlink shouldn't iput()
32 * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir()
33 * exposed a problem in readdir
34 * 2.1.107 code-freeze spellchecker run
35 * Aug 1998 2.1.118+ VFS changes
36 * Sep 1998 2.1.122 another VFS change (follow_link)
37 * Apr 1999 2.2.7 no more EBADF checking in
38 * lookup/readdir, use ERR_PTR
39 * Jun 1999 2.3.6 d_alloc_root use changed
40 * 2.3.9 clean up usage of ENOENT/negative
41 * dentries in lookup
42 * clean up page flags setting
43 * (error, uptodate, locking) in
44 * in readpage
45 * use init_special_inode for
46 * fifos/sockets (and streamline) in
47 * read_inode, fix _ops table order
48 * Aug 1999 2.3.16 __initfunc() => __init change
49 * Oct 1999 2.3.24 page->owner hack obsoleted
50 * Nov 1999 2.3.27 2.3.25+ page->offset => index change
51 *
52 *
53 * This program is free software; you can redistribute it and/or
54 * modify it under the terms of the GNU General Public Licence
55 * as published by the Free Software Foundation; either version
56 * 2 of the Licence, or (at your option) any later version.
57 */
58
59#include <linux/module.h>
60#include <linux/string.h>
61#include <linux/fs.h>
62#include <linux/time.h>
63#include <linux/slab.h>
64#include <linux/init.h>
65#include <linux/blkdev.h>
66#include <linux/parser.h>
67#include <linux/mount.h>
68#include <linux/namei.h>
69#include <linux/statfs.h>
70#include <linux/mtd/super.h>
71#include <linux/ctype.h>
72#include <linux/highmem.h>
73#include <linux/pagemap.h>
74#include <linux/uaccess.h>
75#include "internal.h"
76
77static struct kmem_cache *romfs_inode_cachep;
78
79static const umode_t romfs_modemap[8] = {
80 0, /* hard link */
81 S_IFDIR | 0644, /* directory */
82 S_IFREG | 0644, /* regular file */
83 S_IFLNK | 0777, /* symlink */
84 S_IFBLK | 0600, /* blockdev */
85 S_IFCHR | 0600, /* chardev */
86 S_IFSOCK | 0644, /* socket */
87 S_IFIFO | 0644 /* FIFO */
88};
89
90static const unsigned char romfs_dtype_table[] = {
91 DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
92};
93
94static struct inode *romfs_iget(struct super_block *sb, unsigned long pos);
95
96/*
97 * read a page worth of data from the image
98 */
99static int romfs_readpage(struct file *file, struct page *page)
100{
101 struct inode *inode = page->mapping->host;
102 loff_t offset, size;
103 unsigned long fillsize, pos;
104 void *buf;
105 int ret;
106
107 buf = kmap(page);
108 if (!buf)
109 return -ENOMEM;
110
111 /* 32 bit warning -- but not for us :) */
112 offset = page_offset(page);
113 size = i_size_read(inode);
114 fillsize = 0;
115 ret = 0;
116 if (offset < size) {
117 size -= offset;
118 fillsize = size > PAGE_SIZE ? PAGE_SIZE : size;
119
120 pos = ROMFS_I(inode)->i_dataoffset + offset;
121
122 ret = romfs_dev_read(inode->i_sb, pos, buf, fillsize);
123 if (ret < 0) {
124 SetPageError(page);
125 fillsize = 0;
126 ret = -EIO;
127 }
128 }
129
130 if (fillsize < PAGE_SIZE)
131 memset(buf + fillsize, 0, PAGE_SIZE - fillsize);
132 if (ret == 0)
133 SetPageUptodate(page);
134
135 flush_dcache_page(page);
136 kunmap(page);
137 unlock_page(page);
138 return ret;
139}
140
141static const struct address_space_operations romfs_aops = {
142 .readpage = romfs_readpage
143};
144
145/*
146 * read the entries from a directory
147 */
148static int romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
149{
150 struct inode *i = filp->f_dentry->d_inode;
151 struct romfs_inode ri;
152 unsigned long offset, maxoff;
153 int j, ino, nextfh;
154 int stored = 0;
155 char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
156 int ret;
157
158 maxoff = romfs_maxsize(i->i_sb);
159
160 offset = filp->f_pos;
161 if (!offset) {
162 offset = i->i_ino & ROMFH_MASK;
163 ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE);
164 if (ret < 0)
165 goto out;
166 offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
167 }
168
169 /* Not really failsafe, but we are read-only... */
170 for (;;) {
171 if (!offset || offset >= maxoff) {
172 offset = maxoff;
173 filp->f_pos = offset;
174 goto out;
175 }
176 filp->f_pos = offset;
177
178 /* Fetch inode info */
179 ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE);
180 if (ret < 0)
181 goto out;
182
183 j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE,
184 sizeof(fsname) - 1);
185 if (j < 0)
186 goto out;
187
188 ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j);
189 if (ret < 0)
190 goto out;
191 fsname[j] = '\0';
192
193 ino = offset;
194 nextfh = be32_to_cpu(ri.next);
195 if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
196 ino = be32_to_cpu(ri.spec);
197 if (filldir(dirent, fsname, j, offset, ino,
198 romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0)
199 goto out;
200
201 stored++;
202 offset = nextfh & ROMFH_MASK;
203 }
204
205out:
206 return stored;
207}
208
209/*
210 * look up an entry in a directory
211 */
212static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry,
213 struct nameidata *nd)
214{
215 unsigned long offset, maxoff;
216 struct inode *inode;
217 struct romfs_inode ri;
218 const char *name; /* got from dentry */
219 int len, ret;
220
221 offset = dir->i_ino & ROMFH_MASK;
222 ret = romfs_dev_read(dir->i_sb, offset, &ri, ROMFH_SIZE);
223 if (ret < 0)
224 goto error;
225
226 /* search all the file entries in the list starting from the one
227 * pointed to by the directory's special data */
228 maxoff = romfs_maxsize(dir->i_sb);
229 offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
230
231 name = dentry->d_name.name;
232 len = dentry->d_name.len;
233
234 for (;;) {
235 if (!offset || offset >= maxoff)
236 goto out0;
237
238 ret = romfs_dev_read(dir->i_sb, offset, &ri, sizeof(ri));
239 if (ret < 0)
240 goto error;
241
242 /* try to match the first 16 bytes of name */
243 ret = romfs_dev_strncmp(dir->i_sb, offset + ROMFH_SIZE, name,
244 len);
245 if (ret < 0)
246 goto error;
247 if (ret == 1)
248 break;
249
250 /* next entry */
251 offset = be32_to_cpu(ri.next) & ROMFH_MASK;
252 }
253
254 /* Hard link handling */
255 if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
256 offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
257
258 inode = romfs_iget(dir->i_sb, offset);
259 if (IS_ERR(inode)) {
260 ret = PTR_ERR(inode);
261 goto error;
262 }
263 goto outi;
264
265 /*
266 * it's a bit funky, _lookup needs to return an error code
267 * (negative) or a NULL, both as a dentry. ENOENT should not
268 * be returned, instead we need to create a negative dentry by
269 * d_add(dentry, NULL); and return 0 as no error.
270 * (Although as I see, it only matters on writable file
271 * systems).
272 */
273out0:
274 inode = NULL;
275outi:
276 d_add(dentry, inode);
277 ret = 0;
278error:
279 return ERR_PTR(ret);
280}
281
282static const struct file_operations romfs_dir_operations = {
283 .read = generic_read_dir,
284 .readdir = romfs_readdir,
285};
286
287static struct inode_operations romfs_dir_inode_operations = {
288 .lookup = romfs_lookup,
289};
290
291/*
292 * get a romfs inode based on its position in the image (which doubles as the
293 * inode number)
294 */
295static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
296{
297 struct romfs_inode_info *inode;
298 struct romfs_inode ri;
299 struct inode *i;
300 unsigned long nlen;
301 unsigned nextfh, ret;
302 umode_t mode;
303
304 /* we might have to traverse a chain of "hard link" file entries to get
305 * to the actual file */
306 for (;;) {
307 ret = romfs_dev_read(sb, pos, &ri, sizeof(ri));
308 if (ret < 0)
309 goto error;
310
311 /* XXX: do romfs_checksum here too (with name) */
312
313 nextfh = be32_to_cpu(ri.next);
314 if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
315 break;
316
317 pos = be32_to_cpu(ri.spec) & ROMFH_MASK;
318 }
319
320 /* determine the length of the filename */
321 nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN);
322 if (IS_ERR_VALUE(nlen))
323 goto eio;
324
325 /* get an inode for this image position */
326 i = iget_locked(sb, pos);
327 if (!i)
328 return ERR_PTR(-ENOMEM);
329
330 if (!(i->i_state & I_NEW))
331 return i;
332
333 /* precalculate the data offset */
334 inode = ROMFS_I(i);
335 inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK;
336 inode->i_dataoffset = pos + inode->i_metasize;
337
338 i->i_nlink = 1; /* Hard to decide.. */
339 i->i_size = be32_to_cpu(ri.size);
340 i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0;
341 i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0;
342
343 /* set up mode and ops */
344 mode = romfs_modemap[nextfh & ROMFH_TYPE];
345
346 switch (nextfh & ROMFH_TYPE) {
347 case ROMFH_DIR:
348 i->i_size = ROMFS_I(i)->i_metasize;
349 i->i_op = &romfs_dir_inode_operations;
350 i->i_fop = &romfs_dir_operations;
351 if (nextfh & ROMFH_EXEC)
352 mode |= S_IXUGO;
353 break;
354 case ROMFH_REG:
355 i->i_fop = &romfs_ro_fops;
356 i->i_data.a_ops = &romfs_aops;
357 if (i->i_sb->s_mtd)
358 i->i_data.backing_dev_info =
359 i->i_sb->s_mtd->backing_dev_info;
360 if (nextfh & ROMFH_EXEC)
361 mode |= S_IXUGO;
362 break;
363 case ROMFH_SYM:
364 i->i_op = &page_symlink_inode_operations;
365 i->i_data.a_ops = &romfs_aops;
366 mode |= S_IRWXUGO;
367 break;
368 default:
369 /* depending on MBZ for sock/fifos */
370 nextfh = be32_to_cpu(ri.spec);
371 init_special_inode(i, mode, MKDEV(nextfh >> 16,
372 nextfh & 0xffff));
373 break;
374 }
375
376 i->i_mode = mode;
377
378 unlock_new_inode(i);
379 return i;
380
381eio:
382 ret = -EIO;
383error:
384 printk(KERN_ERR "ROMFS: read error for inode 0x%lx\n", pos);
385 return ERR_PTR(ret);
386}
387
388/*
389 * allocate a new inode
390 */
391static struct inode *romfs_alloc_inode(struct super_block *sb)
392{
393 struct romfs_inode_info *inode;
394 inode = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL);
395 return inode ? &inode->vfs_inode : NULL;
396}
397
398/*
399 * return a spent inode to the slab cache
400 */
401static void romfs_destroy_inode(struct inode *inode)
402{
403 kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode));
404}
405
406/*
407 * get filesystem statistics
408 */
409static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
410{
411 buf->f_type = ROMFS_MAGIC;
412 buf->f_namelen = ROMFS_MAXFN;
413 buf->f_bsize = ROMBSIZE;
414 buf->f_bfree = buf->f_bavail = buf->f_ffree;
415 buf->f_blocks =
416 (romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS;
417 return 0;
418}
419
420/*
421 * remounting must involve read-only
422 */
423static int romfs_remount(struct super_block *sb, int *flags, char *data)
424{
425 *flags |= MS_RDONLY;
426 return 0;
427}
428
429static const struct super_operations romfs_super_ops = {
430 .alloc_inode = romfs_alloc_inode,
431 .destroy_inode = romfs_destroy_inode,
432 .statfs = romfs_statfs,
433 .remount_fs = romfs_remount,
434};
435
436/*
437 * checksum check on part of a romfs filesystem
438 */
439static __u32 romfs_checksum(const void *data, int size)
440{
441 const __be32 *ptr = data;
442 __u32 sum;
443
444 sum = 0;
445 size >>= 2;
446 while (size > 0) {
447 sum += be32_to_cpu(*ptr++);
448 size--;
449 }
450 return sum;
451}
452
453/*
454 * fill in the superblock
455 */
456static int romfs_fill_super(struct super_block *sb, void *data, int silent)
457{
458 struct romfs_super_block *rsb;
459 struct inode *root;
460 unsigned long pos, img_size;
461 const char *storage;
462 size_t len;
463 int ret;
464
465#ifdef CONFIG_BLOCK
466 if (!sb->s_mtd) {
467 sb_set_blocksize(sb, ROMBSIZE);
468 } else {
469 sb->s_blocksize = ROMBSIZE;
470 sb->s_blocksize_bits = blksize_bits(ROMBSIZE);
471 }
472#endif
473
474 sb->s_maxbytes = 0xFFFFFFFF;
475 sb->s_magic = ROMFS_MAGIC;
476 sb->s_flags |= MS_RDONLY | MS_NOATIME;
477 sb->s_op = &romfs_super_ops;
478
479 /* read the image superblock and check it */
480 rsb = kmalloc(512, GFP_KERNEL);
481 if (!rsb)
482 return -ENOMEM;
483
484 sb->s_fs_info = (void *) 512;
485 ret = romfs_dev_read(sb, 0, rsb, 512);
486 if (ret < 0)
487 goto error_rsb;
488
489 img_size = be32_to_cpu(rsb->size);
490
491 if (sb->s_mtd && img_size > sb->s_mtd->size)
492 goto error_rsb_inval;
493
494 sb->s_fs_info = (void *) img_size;
495
496 if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 ||
497 img_size < ROMFH_SIZE) {
498 if (!silent)
499 printk(KERN_WARNING "VFS:"
500 " Can't find a romfs filesystem on dev %s.\n",
501 sb->s_id);
502 goto error_rsb_inval;
503 }
504
505 if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) {
506 printk(KERN_ERR "ROMFS: bad initial checksum on dev %s.\n",
507 sb->s_id);
508 goto error_rsb_inval;
509 }
510
511 storage = sb->s_mtd ? "MTD" : "the block layer";
512
513 len = strnlen(rsb->name, ROMFS_MAXFN);
514 if (!silent)
515 printk(KERN_NOTICE "ROMFS: Mounting image '%*.*s' through %s\n",
516 (unsigned) len, (unsigned) len, rsb->name, storage);
517
518 kfree(rsb);
519 rsb = NULL;
520
521 /* find the root directory */
522 pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK;
523
524 root = romfs_iget(sb, pos);
525 if (!root)
526 goto error;
527
528 sb->s_root = d_alloc_root(root);
529 if (!sb->s_root)
530 goto error_i;
531
532 return 0;
533
534error_i:
535 iput(root);
536error:
537 return -EINVAL;
538error_rsb_inval:
539 ret = -EINVAL;
540error_rsb:
541 return ret;
542}
543
544/*
545 * get a superblock for mounting
546 */
547static int romfs_get_sb(struct file_system_type *fs_type,
548 int flags, const char *dev_name,
549 void *data, struct vfsmount *mnt)
550{
551 int ret = -EINVAL;
552
553#ifdef CONFIG_ROMFS_ON_MTD
554 ret = get_sb_mtd(fs_type, flags, dev_name, data, romfs_fill_super,
555 mnt);
556#endif
557#ifdef CONFIG_ROMFS_ON_BLOCK
558 if (ret == -EINVAL)
559 ret = get_sb_bdev(fs_type, flags, dev_name, data,
560 romfs_fill_super, mnt);
561#endif
562 return ret;
563}
564
565/*
566 * destroy a romfs superblock in the appropriate manner
567 */
568static void romfs_kill_sb(struct super_block *sb)
569{
570#ifdef CONFIG_ROMFS_ON_MTD
571 if (sb->s_mtd) {
572 kill_mtd_super(sb);
573 return;
574 }
575#endif
576#ifdef CONFIG_ROMFS_ON_BLOCK
577 if (sb->s_bdev) {
578 kill_block_super(sb);
579 return;
580 }
581#endif
582}
583
584static struct file_system_type romfs_fs_type = {
585 .owner = THIS_MODULE,
586 .name = "romfs",
587 .get_sb = romfs_get_sb,
588 .kill_sb = romfs_kill_sb,
589 .fs_flags = FS_REQUIRES_DEV,
590};
591
592/*
593 * inode storage initialiser
594 */
595static void romfs_i_init_once(void *_inode)
596{
597 struct romfs_inode_info *inode = _inode;
598
599 inode_init_once(&inode->vfs_inode);
600}
601
602/*
603 * romfs module initialisation
604 */
605static int __init init_romfs_fs(void)
606{
607 int ret;
608
609 printk(KERN_INFO "ROMFS MTD (C) 2007 Red Hat, Inc.\n");
610
611 romfs_inode_cachep =
612 kmem_cache_create("romfs_i",
613 sizeof(struct romfs_inode_info), 0,
614 SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
615 romfs_i_init_once);
616
617 if (!romfs_inode_cachep) {
618 printk(KERN_ERR
619 "ROMFS error: Failed to initialise inode cache\n");
620 return -ENOMEM;
621 }
622 ret = register_filesystem(&romfs_fs_type);
623 if (ret) {
624 printk(KERN_ERR "ROMFS error: Failed to register filesystem\n");
625 goto error_register;
626 }
627 return 0;
628
629error_register:
630 kmem_cache_destroy(romfs_inode_cachep);
631 return ret;
632}
633
634/*
635 * romfs module removal
636 */
637static void __exit exit_romfs_fs(void)
638{
639 unregister_filesystem(&romfs_fs_type);
640 kmem_cache_destroy(romfs_inode_cachep);
641}
642
643module_init(init_romfs_fs);
644module_exit(exit_romfs_fs);
645
646MODULE_DESCRIPTION("Direct-MTD Capable RomFS");
647MODULE_AUTHOR("Red Hat, Inc.");
648MODULE_LICENSE("GPL"); /* Actually dual-licensed, but it doesn't matter for */