diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /fs/romfs | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'fs/romfs')
-rw-r--r-- | fs/romfs/Makefile | 14 | ||||
-rw-r--r-- | fs/romfs/inode.c | 666 |
2 files changed, 680 insertions, 0 deletions
diff --git a/fs/romfs/Makefile b/fs/romfs/Makefile new file mode 100644 index 000000000..70fd45a4e --- /dev/null +++ b/fs/romfs/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the linux romfs-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := romfs.o +O_OBJS := inode.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c new file mode 100644 index 000000000..f3f73c66e --- /dev/null +++ b/fs/romfs/inode.c @@ -0,0 +1,666 @@ +/* + * ROMFS file system, Linux implementation + * + * Copyright (C) 1997 Janos Farkas <chexum@shadow.banki.hu> + * + * Using parts of the minix filesystem + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and parts of the affs filesystem additionally + * Copyright (C) 1993 Ray Burr + * Copyright (C) 1996 Hans-Joachim Widmaier + * + * 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. + * + * Changes + * Changed for 2.1.19 modules + * Jan 1997 Initial release + */ + +/* todo: + * use malloced memory for file names? + * considering write access... + * network (tftp) files? + */ + +/* + * Sorry about some optimizations and for some goto's. I just wanted + * to squeeze some more bytes out of this code.. :) + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/romfs_fs.h> +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/init.h> + +#include <asm/uaccess.h> + +static int inline min(int a, int b) +{ + return a<b ? a : b; +} + +static __s32 +romfs_checksum(void *data, int size) +{ + __s32 sum, *ptr; + + sum = 0; ptr = data; + size>>=2; + while (size>0) { + sum += ntohl(*ptr++); + size--; + } + return sum; +} + +static struct super_operations romfs_ops; + +static struct super_block * +romfs_read_super(struct super_block *s, void *data, int silent) +{ + struct buffer_head *bh; + kdev_t dev = s->s_dev; + struct romfs_super_block *rsb; + int sz; + + MOD_INC_USE_COUNT; + + /* I would parse the options, but there are none.. :) */ + + lock_super(s); + set_blocksize(dev, ROMBSIZE); + s->s_blocksize = ROMBSIZE; + s->s_blocksize_bits = ROMBSBITS; + bh = bread(dev, 0, ROMBSIZE); + if (!bh) { + printk ("romfs: unable to read superblock\n"); + goto outnobh; + } + + rsb = (struct romfs_super_block *)bh->b_data; + sz = ntohl(rsb->size); + if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 + || sz < ROMFH_SIZE) { + if (!silent) + printk ("VFS: Can't find a romfs filesystem on dev " + "%s.\n", kdevname(dev)); + goto out; + } + if (romfs_checksum(rsb, min(sz,512))) { + printk ("romfs: bad initial checksum on dev " + "%s.\n", kdevname(dev)); + } + + s->s_magic = ROMFS_MAGIC; + s->u.romfs_sb.s_maxsize = sz; + + s->s_flags |= MS_RDONLY; + + /* Find the start of the fs */ + sz = (ROMFH_SIZE + + strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD) + & ROMFH_MASK; + + brelse(bh); + + s->s_op = &romfs_ops; + + unlock_super(s); + + if (!(s->s_mounted = iget(s, sz))) + goto outnobh; + + /* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */ + if (0) { +out: + brelse(bh); +outnobh: + s->s_dev = 0; + unlock_super(s); + MOD_DEC_USE_COUNT; + s = NULL; + } + + return s; +} + +/* Nothing to do.. */ + +static void +romfs_put_super(struct super_block *sb) +{ + lock_super(sb); + sb->s_dev = 0; + unlock_super(sb); + MOD_DEC_USE_COUNT; + return; +} + + +/* That's simple too. */ + +static void +romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize) +{ + struct statfs tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.f_type = ROMFS_MAGIC; + tmp.f_bsize = ROMBSIZE; + tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS; + copy_to_user(buf, &tmp, bufsize); +} + +static int +romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count) +{ + struct buffer_head *bh; + unsigned long avail, maxsize, res; + + maxsize = i->i_sb->u.romfs_sb.s_maxsize; + if (offset >= maxsize) + return -1; + + /* strnlen is almost always valid */ + if (count > maxsize || offset+count > maxsize) + count = maxsize-offset; + + bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE); + if (!bh) + return -1; /* error */ + + avail = ROMBSIZE - (offset & ROMBMASK); + maxsize = min(count, avail); + res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize); + brelse(bh); + + if (res < maxsize) + return res; /* found all of it */ + + while (res < count) { + offset += maxsize; + + bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE); + if (!bh) + return -1; + maxsize = min(count-res, ROMBSIZE); + avail = strnlen(bh->b_data, maxsize); + res += avail; + brelse(bh); + if (avail < maxsize) + return res; + } + return res; +} + +static int +romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count) +{ + struct buffer_head *bh; + unsigned long avail, maxsize, res; + + maxsize = i->i_sb->u.romfs_sb.s_maxsize; + if (offset >= maxsize || count > maxsize || offset+count>maxsize) + return -1; + + bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE); + if (!bh) + return -1; /* error */ + + avail = ROMBSIZE - (offset & ROMBMASK); + maxsize = min(count, avail); + memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize); + brelse(bh); + + res = maxsize; /* all of it */ + + while (res < count) { + offset += maxsize; + dest += maxsize; + + bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE); + if (!bh) + return -1; + maxsize = min(count-res, ROMBSIZE); + memcpy(dest, bh->b_data, maxsize); + brelse(bh); + res += maxsize; + } + return res; +} + +/* Directory operations */ + +static int +romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir) +{ + struct romfs_inode ri; + unsigned long offset, maxoff; + int j, ino, nextfh; + int stored = 0; + char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ + + if (!i || !S_ISDIR(i->i_mode)) + return -EBADF; + + maxoff = i->i_sb->u.romfs_sb.s_maxsize; + + offset = filp->f_pos; + if (!offset) { + offset = i->i_ino & ROMFH_MASK; + if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0) + return stored; + offset = ntohl(ri.spec) & ROMFH_MASK; + } + + /* Not really failsafe, but we are read-only... */ + for(;;) { + if (!offset || offset >= maxoff) { + offset = 0xffffffff; + filp->f_pos = offset; + return stored; + } + filp->f_pos = offset; + + /* Fetch inode info */ + if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0) + return stored; + + j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1); + if (j < 0) + return stored; + + fsname[j]=0; + romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j); + + ino = offset; + nextfh = ntohl(ri.next); + if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) + ino = ntohl(ri.spec); + if (filldir(dirent, fsname, j, offset, ino) < 0) { + return stored; + } + stored++; + offset = nextfh & ROMFH_MASK; + } +} + +static int +romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result) +{ + unsigned long offset, maxoff; + int fslen, res; + char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ + struct romfs_inode ri; + + *result = NULL; + if (!dir || !S_ISDIR(dir->i_mode)) { + res = -EBADF; + goto out; + } + + offset = dir->i_ino & ROMFH_MASK; + if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) { + res = -ENOENT; + goto out; + } + + maxoff = dir->i_sb->u.romfs_sb.s_maxsize; + offset = ntohl(ri.spec) & ROMFH_MASK; + + for(;;) { + if (!offset || offset >= maxoff + || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) { + res = -ENOENT; + goto out; + } + + /* try to match the first 16 bytes of name */ + fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE); + if (len < ROMFH_SIZE) { + if (len == fslen) { + /* both are shorter, and same size */ + romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1); + if (strncmp (name, fsname, len) == 0) + break; + } + } else if (fslen >= ROMFH_SIZE) { + /* both are longer; XXX optimize max size */ + fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1); + if (len == fslen) { + romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1); + if (strncmp(name, fsname, len) == 0) + break; + } + } + /* next entry */ + offset = ntohl(ri.next) & ROMFH_MASK; + } + + /* Hard link handling */ + if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD) + offset = ntohl(ri.spec) & ROMFH_MASK; + + res = 0; + if (!(*result = iget(dir->i_sb, offset))) + res = -EACCES; + +out: + iput(dir); + return res; +} + +/* + * Ok, we do readpage, to be able to execute programs. Unfortunately, + * bmap is not applicable, since we have looser alignments. + * + * XXX I'm not quite sure that I need to muck around the PG_xx bits.. + */ + +static int +romfs_readpage(struct inode * inode, struct page * page) +{ + unsigned long buf; + unsigned long offset, avail, readlen; + int result = -EIO; + + buf = page_address(page); + atomic_inc(&page->count); + offset = page->offset; + if (offset < inode->i_size) { + avail = inode->i_size-offset; + readlen = min(avail, PAGE_SIZE); + if (romfs_copyfrom(inode, (void *)buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) { + if (readlen < PAGE_SIZE) { + memset((void *)(buf+readlen),0,PAGE_SIZE-readlen); + } + result = 0; + set_bit(PG_uptodate, &page->flags); + } else { + memset((void *)buf, 0, PAGE_SIZE); + } + } + free_page(buf); + return result; +} + +static int +romfs_readlink(struct inode *inode, char *buffer, int len) +{ + int mylen; + char buf[ROMFS_MAXFN]; /* XXX dynamic */ + + if (!inode || !S_ISLNK(inode->i_mode)) { + mylen = -EBADF; + goto out; + } + + mylen = min(sizeof(buf), inode->i_size); + + if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, mylen) <= 0) { + mylen = -EIO; + goto out; + } + copy_to_user(buffer, buf, mylen); + +out: + iput(inode); + return mylen; +} + +static int +romfs_follow_link(struct inode *dir, struct inode *inode, + int flag, int mode, struct inode **res_inode) +{ + int error, len; + char *buf; + + *res_inode = NULL; + if (!dir) { + dir = current->fs->root; + dir->i_count++; + } + + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!S_ISLNK(inode->i_mode)) { + *res_inode = inode; + iput(dir); + return 0; + } + if (current->link_count > 5) { + iput(inode); + iput(dir); + return -ELOOP; + } + + /* Eek. Short enough. */ + len = inode->i_size; + if (!(buf = kmalloc(len+1, GFP_KERNEL))) { + iput(inode); + iput(dir); + /* correct? spin? */ + return -EAGAIN; + } + error = romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, len); + if (error != len) { + iput(inode); + iput(dir); + error = -EIO; + } else { + iput(inode); + buf[len] = 0; + current->link_count++; + error = open_namei(buf, flag, mode, res_inode, dir); + current->link_count--; + } + + kfree(buf); + return error; +} + +/* Mapping from our types to the kernel */ + +static struct file_operations romfs_file_operations = { + NULL, /* lseek - default */ + generic_file_read, /* read */ + NULL, /* write - bad */ + NULL, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl */ + generic_file_mmap, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +static struct inode_operations romfs_file_inode_operations = { + &romfs_file_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + romfs_readpage, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap -- not really */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ +}; + +static struct file_operations romfs_dir_operations = { + NULL, /* lseek - default */ + NULL, /* read */ + NULL, /* write - bad */ + romfs_readdir, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +/* Merged dir/symlink op table. readdir/lookup/readlink/follow_link + * will protect from type mismatch. + */ + +static struct inode_operations romfs_dirlink_inode_operations = { + &romfs_dir_operations, + NULL, /* create */ + romfs_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + romfs_readlink, /* readlink */ + romfs_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ +}; + +static mode_t romfs_modemap[] = +{ + 0, S_IFDIR, S_IFREG, S_IFLNK+0777, + S_IFBLK, S_IFCHR, S_IFSOCK, S_IFIFO +}; + +static struct inode_operations *romfs_inoops[] = +{ + NULL, /* hardlink, handled elsewhere */ + &romfs_dirlink_inode_operations, + &romfs_file_inode_operations, + &romfs_dirlink_inode_operations, + &blkdev_inode_operations, /* standard handlers */ + &chrdev_inode_operations, + NULL, /* socket */ + NULL, /* fifo */ +}; + +static void +romfs_read_inode(struct inode *i) +{ + int nextfh, ino; + struct romfs_inode ri; + + i->i_op = NULL; + + ino = i->i_ino & ROMFH_MASK; + + /* Loop for finding the real hard link */ + for(;;) { + if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) { + printk("romfs: read error for inode 0x%x\n", ino); + return; + } + nextfh = ntohl(ri.next); + if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) + break; + + ino = ntohl(ri.spec) & ROMFH_MASK; + } + + i->i_nlink = 1; /* Hard to decide.. */ + i->i_size = ntohl(ri.size); + i->i_mtime = i->i_atime = i->i_ctime = 0; + i->i_uid = i->i_gid = 0; + + i->i_op = romfs_inoops[nextfh & ROMFH_TYPE]; + + /* Precalculate the data offset */ + ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN); + if (ino >= 0) + ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK); + else + ino = 0; + i->u.romfs_i.i_metasize = ino; + i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK); + + /* Compute permissions */ + ino = S_IRUGO|S_IWUSR; + ino |= romfs_modemap[nextfh & ROMFH_TYPE]; + if (nextfh & ROMFH_EXEC) { + ino |= S_IXUGO; + } + i->i_mode = ino; + + if (S_ISFIFO(ino)) + init_fifo(i); + else if (S_ISDIR(ino)) + i->i_size = i->u.romfs_i.i_metasize; + else if (S_ISBLK(ino) || S_ISCHR(ino)) { + i->i_mode &= ~(S_IRWXG|S_IRWXO); + ino = ntohl(ri.spec); + i->i_rdev = MKDEV(ino>>16,ino&0xffff); + } +} + +static struct super_operations romfs_ops = { + romfs_read_inode, /* read inode */ + NULL, /* notify change */ + NULL, /* write inode */ + NULL, /* put inode */ + romfs_put_super, /* put super */ + NULL, /* write super */ + romfs_statfs, /* statfs */ + NULL /* remount */ +}; + +static struct file_system_type romfs_fs_type = { + romfs_read_super, "romfs", 1, NULL +}; + +__initfunc(int init_romfs_fs(void)) +{ + return register_filesystem(&romfs_fs_type); +} + +#ifdef MODULE + +/* Yes, works even as a module... :) */ + +EXPORT_NO_SYMBOLS; + +int +init_module(void) +{ + return init_romfs_fs(); +} + +void +cleanup_module(void) +{ + unregister_filesystem(&romfs_fs_type); +} +#endif |