pre-2.3.4..
[davej-history.git] / fs / romfs / inode.c
blobfd374842eeaff2b952277bca04a3ed3007b31dbc
1 /*
2 * ROMFS file system, Linux implementation
4 * Copyright (C) 1997 Janos Farkas <chexum@shadow.banki.hu>
6 * Using parts of the minix filesystem
7 * Copyright (C) 1991, 1992 Linus Torvalds
9 * and parts of the affs filesystem additionally
10 * Copyright (C) 1993 Ray Burr
11 * Copyright (C) 1996 Hans-Joachim Widmaier
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version
16 * 2 of the License, or (at your option) any later version.
18 * Changes
19 * Changed for 2.1.19 modules
20 * Jan 1997 Initial release
21 * Jun 1997 2.1.43+ changes
22 * Proper page locking in readpage
23 * Changed to work with 2.1.45+ fs
24 * Jul 1997 Fixed follow_link
25 * 2.1.47
26 * lookup shouldn't return -ENOENT
27 * from Horst von Brand:
28 * fail on wrong checksum
29 * double unlock_super was possible
30 * correct namelen for statfs
31 * spotted by Bill Hawes:
32 * readlink shouldn't iput()
33 * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir()
34 * exposed a problem in readdir
35 * 2.1.107 code-freeze spellchecker run
36 * Aug 1998 2.1.118+ VFS changes
39 /* todo:
40 * - see Documentation/filesystems/romfs.txt
41 * - use allocated, not stack memory for file names?
42 * - considering write access...
43 * - network (tftp) files?
44 * - merge back some _op tables
48 * Sorry about some optimizations and for some goto's. I just wanted
49 * to squeeze some more bytes out of this code.. :)
52 #include <linux/module.h>
53 #include <linux/types.h>
54 #include <linux/errno.h>
55 #include <linux/malloc.h>
56 #include <linux/romfs_fs.h>
57 #include <linux/fs.h>
58 #include <linux/locks.h>
59 #include <linux/init.h>
61 #include <asm/uaccess.h>
63 static int inline min(int a, int b)
65 return a<b ? a : b;
68 static __s32
69 romfs_checksum(void *data, int size)
71 __s32 sum, *ptr;
73 sum = 0; ptr = data;
74 size>>=2;
75 while (size>0) {
76 sum += ntohl(*ptr++);
77 size--;
79 return sum;
82 static struct super_operations romfs_ops;
84 static struct super_block *
85 romfs_read_super(struct super_block *s, void *data, int silent)
87 struct buffer_head *bh;
88 kdev_t dev = s->s_dev;
89 struct romfs_super_block *rsb;
90 int sz;
92 MOD_INC_USE_COUNT;
94 /* I would parse the options here, but there are none.. :) */
96 lock_super(s);
97 set_blocksize(dev, ROMBSIZE);
98 s->s_blocksize = ROMBSIZE;
99 s->s_blocksize_bits = ROMBSBITS;
100 bh = bread(dev, 0, ROMBSIZE);
101 if (!bh) {
102 /* XXX merge with other printk? */
103 printk ("romfs: unable to read superblock\n");
104 goto outnobh;
107 rsb = (struct romfs_super_block *)bh->b_data;
108 sz = ntohl(rsb->size);
109 if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
110 || sz < ROMFH_SIZE) {
111 if (!silent)
112 printk ("VFS: Can't find a romfs filesystem on dev "
113 "%s.\n", kdevname(dev));
114 goto out;
116 if (romfs_checksum(rsb, min(sz,512))) {
117 printk ("romfs: bad initial checksum on dev "
118 "%s.\n", kdevname(dev));
119 goto out;
122 s->s_magic = ROMFS_MAGIC;
123 s->u.romfs_sb.s_maxsize = sz;
125 s->s_flags |= MS_RDONLY;
127 /* Find the start of the fs */
128 sz = (ROMFH_SIZE +
129 strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
130 & ROMFH_MASK;
132 brelse(bh);
134 s->s_op = &romfs_ops;
135 s->s_root = d_alloc_root(iget(s, sz), NULL);
137 if (!s->s_root)
138 goto outnobh;
140 unlock_super(s);
142 /* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */
143 if (0) {
144 out:
145 brelse(bh);
146 outnobh:
147 s->s_dev = 0;
148 unlock_super(s);
149 MOD_DEC_USE_COUNT;
150 s = NULL;
153 return s;
156 /* Nothing to do.. */
158 static void
159 romfs_put_super(struct super_block *sb)
161 MOD_DEC_USE_COUNT;
162 return;
165 /* That's simple too. */
167 static int
168 romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
170 struct statfs tmp;
172 memset(&tmp, 0, sizeof(tmp));
173 tmp.f_type = ROMFS_MAGIC;
174 tmp.f_bsize = ROMBSIZE;
175 tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
176 tmp.f_namelen = ROMFS_MAXFN;
177 return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
180 /* some helper routines */
182 static int
183 romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
185 struct buffer_head *bh;
186 unsigned long avail, maxsize, res;
188 maxsize = i->i_sb->u.romfs_sb.s_maxsize;
189 if (offset >= maxsize)
190 return -1;
192 /* strnlen is almost always valid */
193 if (count > maxsize || offset+count > maxsize)
194 count = maxsize-offset;
196 bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
197 if (!bh)
198 return -1; /* error */
200 avail = ROMBSIZE - (offset & ROMBMASK);
201 maxsize = min(count, avail);
202 res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
203 brelse(bh);
205 if (res < maxsize)
206 return res; /* found all of it */
208 while (res < count) {
209 offset += maxsize;
211 bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
212 if (!bh)
213 return -1;
214 maxsize = min(count-res, ROMBSIZE);
215 avail = strnlen(bh->b_data, maxsize);
216 res += avail;
217 brelse(bh);
218 if (avail < maxsize)
219 return res;
221 return res;
224 static int
225 romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
227 struct buffer_head *bh;
228 unsigned long avail, maxsize, res;
230 maxsize = i->i_sb->u.romfs_sb.s_maxsize;
231 if (offset >= maxsize || count > maxsize || offset+count>maxsize)
232 return -1;
234 bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
235 if (!bh)
236 return -1; /* error */
238 avail = ROMBSIZE - (offset & ROMBMASK);
239 maxsize = min(count, avail);
240 memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
241 brelse(bh);
243 res = maxsize; /* all of it */
245 while (res < count) {
246 offset += maxsize;
247 dest += maxsize;
249 bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
250 if (!bh)
251 return -1;
252 maxsize = min(count-res, ROMBSIZE);
253 memcpy(dest, bh->b_data, maxsize);
254 brelse(bh);
255 res += maxsize;
257 return res;
260 static int
261 romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
263 struct inode *i = filp->f_dentry->d_inode;
264 struct romfs_inode ri;
265 unsigned long offset, maxoff;
266 int j, ino, nextfh;
267 int stored = 0;
268 char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
270 maxoff = i->i_sb->u.romfs_sb.s_maxsize;
272 offset = filp->f_pos;
273 if (!offset) {
274 offset = i->i_ino & ROMFH_MASK;
275 if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
276 return stored;
277 offset = ntohl(ri.spec) & ROMFH_MASK;
280 /* Not really failsafe, but we are read-only... */
281 for(;;) {
282 if (!offset || offset >= maxoff) {
283 offset = maxoff;
284 filp->f_pos = offset;
285 return stored;
287 filp->f_pos = offset;
289 /* Fetch inode info */
290 if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
291 return stored;
293 j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
294 if (j < 0)
295 return stored;
297 fsname[j]=0;
298 romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
300 ino = offset;
301 nextfh = ntohl(ri.next);
302 if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
303 ino = ntohl(ri.spec);
304 if (filldir(dirent, fsname, j, offset, ino) < 0) {
305 return stored;
307 stored++;
308 offset = nextfh & ROMFH_MASK;
312 static struct dentry *
313 romfs_lookup(struct inode *dir, struct dentry *dentry)
315 unsigned long offset, maxoff;
316 int fslen, res;
317 struct inode *inode;
318 char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
319 struct romfs_inode ri;
320 const char *name; /* got from dentry */
321 int len;
323 res = 0; /* instead of ENOENT */
324 offset = dir->i_ino & ROMFH_MASK;
325 if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
326 goto out;
328 maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
329 offset = ntohl(ri.spec) & ROMFH_MASK;
331 /* OK, now find the file whose name is in "dentry" in the
332 * directory specified by "dir". */
334 name = dentry->d_name.name;
335 len = dentry->d_name.len;
337 for(;;) {
338 if (!offset || offset >= maxoff
339 || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
340 goto out;
342 /* try to match the first 16 bytes of name */
343 fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
344 if (len < ROMFH_SIZE) {
345 if (len == fslen) {
346 /* both are shorter, and same size */
347 romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
348 if (strncmp (name, fsname, len) == 0)
349 break;
351 } else if (fslen >= ROMFH_SIZE) {
352 /* both are longer; XXX optimize max size */
353 fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
354 if (len == fslen) {
355 romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
356 if (strncmp(name, fsname, len) == 0)
357 break;
360 /* next entry */
361 offset = ntohl(ri.next) & ROMFH_MASK;
364 /* Hard link handling */
365 if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
366 offset = ntohl(ri.spec) & ROMFH_MASK;
368 if ((inode = iget(dir->i_sb, offset))==NULL) {
369 res = -EACCES;
370 } else {
371 d_add(dentry, inode);
374 out:
375 return ERR_PTR(res);
379 * Ok, we do readpage, to be able to execute programs. Unfortunately,
380 * we can't use bmap, since we have looser alignments.
383 static int
384 romfs_readpage(struct file * file, struct page * page)
386 struct dentry *dentry = file->f_dentry;
387 struct inode *inode = dentry->d_inode;
388 unsigned long buf;
389 unsigned long offset, avail, readlen;
390 int result = -EIO;
392 atomic_inc(&page->count);
393 set_bit(PG_locked, &page->flags);
395 buf = page_address(page);
396 clear_bit(PG_uptodate, &page->flags);
397 clear_bit(PG_error, &page->flags);
398 offset = page->offset;
399 if (offset < inode->i_size) {
400 avail = inode->i_size-offset;
401 readlen = min(avail, PAGE_SIZE);
402 if (romfs_copyfrom(inode, (void *)buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
403 if (readlen < PAGE_SIZE) {
404 memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
406 set_bit(PG_uptodate, &page->flags);
407 result = 0;
410 if (result) {
411 set_bit(PG_error, &page->flags);
412 memset((void *)buf, 0, PAGE_SIZE);
415 clear_bit(PG_locked, &page->flags);
416 wake_up(&page->wait);
417 free_page(buf);
419 return result;
422 static int
423 romfs_readlink(struct dentry *dentry, char *buffer, int len)
425 struct inode *inode = dentry->d_inode;
426 int mylen;
427 char buf[ROMFS_MAXFN]; /* XXX dynamic */
429 if (!inode || !S_ISLNK(inode->i_mode)) {
430 mylen = -EBADF;
431 goto out;
434 mylen = min(sizeof(buf), inode->i_size);
436 if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, mylen) <= 0) {
437 mylen = -EIO;
438 goto out;
440 copy_to_user(buffer, buf, mylen);
442 out:
443 return mylen;
446 static struct dentry *romfs_follow_link(struct dentry *dentry,
447 struct dentry *base,
448 unsigned int follow)
450 struct inode *inode = dentry->d_inode;
451 char *link;
452 int len, cnt;
454 len = inode->i_size;
456 dentry = ERR_PTR(-EAGAIN); /* correct? */
457 if (!(link = kmalloc(len+1, GFP_KERNEL)))
458 goto outnobuf;
460 cnt = romfs_copyfrom(inode, link, inode->u.romfs_i.i_dataoffset, len);
461 if (len != cnt) {
462 dentry = ERR_PTR(-EIO);
463 goto out;
464 } else
465 link[len] = 0;
467 dentry = lookup_dentry(link, base, follow);
468 kfree(link);
470 if (0) {
471 out:
472 kfree(link);
473 outnobuf:
474 dput(base);
476 return dentry;
479 /* Mapping from our types to the kernel */
481 static struct file_operations romfs_file_operations = {
482 NULL, /* lseek - default */
483 generic_file_read, /* read */
484 NULL, /* write - bad */
485 NULL, /* readdir */
486 NULL, /* poll - default */
487 NULL, /* ioctl */
488 generic_file_mmap, /* mmap */
489 NULL, /* open */
490 NULL, /* flush */
491 NULL, /* release */
492 NULL, /* fsync */
493 NULL, /* fasync */
494 NULL, /* check_media_change */
495 NULL /* revalidate */
498 static struct inode_operations romfs_file_inode_operations = {
499 &romfs_file_operations,
500 NULL, /* create */
501 NULL, /* lookup */
502 NULL, /* link */
503 NULL, /* unlink */
504 NULL, /* symlink */
505 NULL, /* mkdir */
506 NULL, /* rmdir */
507 NULL, /* mknod */
508 NULL, /* rename */
509 NULL, /* readlink */
510 NULL, /* follow_link */
511 romfs_readpage, /* readpage */
512 NULL, /* writepage */
513 NULL, /* bmap -- not really */
514 NULL, /* truncate */
515 NULL, /* permission */
516 NULL, /* smap */
519 static struct file_operations romfs_dir_operations = {
520 NULL, /* lseek - default */
521 NULL, /* read */
522 NULL, /* write - bad */
523 romfs_readdir, /* readdir */
524 NULL, /* poll - default */
525 NULL, /* ioctl */
526 NULL, /* mmap */
527 NULL, /* open */
528 NULL, /* flush */
529 NULL, /* release */
530 NULL, /* fsync */
531 NULL, /* fasync */
532 NULL, /* check_media_change */
533 NULL /* revalidate */
536 /* Merged dir/symlink op table. readdir/lookup/readlink/follow_link
537 * will protect from type mismatch.
540 static struct inode_operations romfs_dir_inode_operations = {
541 &romfs_dir_operations,
542 NULL, /* create */
543 romfs_lookup, /* lookup */
544 NULL, /* link */
545 NULL, /* unlink */
546 NULL, /* symlink */
547 NULL, /* mkdir */
548 NULL, /* rmdir */
549 NULL, /* mknod */
550 NULL, /* rename */
551 NULL, /* readlink */
552 NULL, /* follow_link */
553 NULL, /* readpage */
554 NULL, /* writepage */
555 NULL, /* bmap */
556 NULL, /* truncate */
557 NULL, /* permission */
558 NULL, /* smap */
561 static struct inode_operations romfs_link_inode_operations = {
562 NULL, /* no file operations on symlinks */
563 NULL, /* create */
564 NULL, /* lookup */
565 NULL, /* link */
566 NULL, /* unlink */
567 NULL, /* symlink */
568 NULL, /* mkdir */
569 NULL, /* rmdir */
570 NULL, /* mknod */
571 NULL, /* rename */
572 romfs_readlink, /* readlink */
573 romfs_follow_link, /* follow_link */
574 NULL, /* readpage */
575 NULL, /* writepage */
576 NULL, /* bmap */
577 NULL, /* truncate */
578 NULL, /* permission */
579 NULL, /* smap */
582 static mode_t romfs_modemap[] =
584 0, S_IFDIR, S_IFREG, S_IFLNK+0777,
585 S_IFBLK, S_IFCHR, S_IFSOCK, S_IFIFO
588 static struct inode_operations *romfs_inoops[] =
590 NULL, /* hardlink, handled elsewhere */
591 &romfs_dir_inode_operations,
592 &romfs_file_inode_operations,
593 &romfs_link_inode_operations,
594 &blkdev_inode_operations, /* standard handlers */
595 &chrdev_inode_operations,
596 NULL, /* socket */
597 NULL, /* fifo */
600 static void
601 romfs_read_inode(struct inode *i)
603 int nextfh, ino;
604 struct romfs_inode ri;
606 ino = i->i_ino & ROMFH_MASK;
607 i->i_op = NULL;
608 i->i_mode = 0;
610 /* Loop for finding the real hard link */
611 for(;;) {
612 if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
613 printk("romfs: read error for inode 0x%x\n", ino);
614 return;
616 /* XXX: do romfs_checksum here too (with name) */
618 nextfh = ntohl(ri.next);
619 if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
620 break;
622 ino = ntohl(ri.spec) & ROMFH_MASK;
625 i->i_nlink = 1; /* Hard to decide.. */
626 i->i_size = ntohl(ri.size);
627 i->i_mtime = i->i_atime = i->i_ctime = 0;
628 i->i_uid = i->i_gid = 0;
630 i->i_op = romfs_inoops[nextfh & ROMFH_TYPE];
632 /* Precalculate the data offset */
633 ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
634 if (ino >= 0)
635 ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
636 else
637 ino = 0;
639 i->u.romfs_i.i_metasize = ino;
640 i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
642 /* Compute permissions */
643 ino = S_IRUGO|S_IWUSR;
644 ino |= romfs_modemap[nextfh & ROMFH_TYPE];
645 if (nextfh & ROMFH_EXEC) {
646 ino |= S_IXUGO;
648 i->i_mode = ino;
650 if (S_ISFIFO(ino))
651 init_fifo(i);
652 else if (S_ISDIR(ino))
653 i->i_size = i->u.romfs_i.i_metasize;
654 else if (S_ISBLK(ino) || S_ISCHR(ino)) {
655 i->i_mode &= ~(S_IRWXG|S_IRWXO);
656 ino = ntohl(ri.spec);
657 i->i_rdev = MKDEV(ino>>16,ino&0xffff);
661 static struct super_operations romfs_ops = {
662 romfs_read_inode, /* read inode */
663 NULL, /* write inode */
664 NULL, /* put inode */
665 NULL, /* delete inode */
666 NULL, /* notify change */
667 romfs_put_super, /* put super */
668 NULL, /* write super */
669 romfs_statfs, /* statfs */
670 NULL /* remount */
673 static struct file_system_type romfs_fs_type = {
674 "romfs",
675 FS_REQUIRES_DEV,
676 romfs_read_super,
677 NULL
680 __initfunc(int init_romfs_fs(void))
682 return register_filesystem(&romfs_fs_type);
685 #ifdef MODULE
687 /* Yes, works even as a module... :) */
689 EXPORT_NO_SYMBOLS;
692 init_module(void)
694 return init_romfs_fs();
697 void
698 cleanup_module(void)
700 unregister_filesystem(&romfs_fs_type);
702 #endif