summaryrefslogtreecommitdiffstats
path: root/fs/efs
diff options
context:
space:
mode:
authorMike Shaver <shaver@ingenia.com>1997-09-16 15:05:24 +0000
committerMike Shaver <shaver@ingenia.com>1997-09-16 15:05:24 +0000
commit4e589e524e0c4c23fd8c6217696bd9983dc8a721 (patch)
treeeecd52c3e576218b98aa69d57cbaaa030e5a4f29 /fs/efs
parent6ce1685bcf39c7a589acdeb9639e71c4dde91ab0 (diff)
EFS integration (read-only, slight problem with symlinks).
Diffstat (limited to 'fs/efs')
-rw-r--r--fs/efs/Makefile17
-rw-r--r--fs/efs/dir.c132
-rw-r--r--fs/efs/file.c218
-rw-r--r--fs/efs/inode.c496
-rw-r--r--fs/efs/namei.c143
-rw-r--r--fs/efs/symlink.c136
6 files changed, 1142 insertions, 0 deletions
diff --git a/fs/efs/Makefile b/fs/efs/Makefile
new file mode 100644
index 000000000..049456899
--- /dev/null
+++ b/fs/efs/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the linux efs-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...
+
+# CFLAGS+=-g -DDEBUG_EFS
+
+O_TARGET := efs.o
+O_OBJS := dir.o file.o inode.o namei.o \
+ symlink.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/efs/dir.c b/fs/efs/dir.c
new file mode 100644
index 000000000..fa07a298f
--- /dev/null
+++ b/fs/efs/dir.c
@@ -0,0 +1,132 @@
+/* dir.c
+
+ directory inode operations for EFS filesystem
+
+ (C)95,96 Christian Vogelgsang
+*/
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/efs_fs.h>
+#include <linux/efs_fs_i.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>
+
+static int efs_readdir(struct file *,void *,filldir_t);
+extern int efs_lookup(struct inode *, struct dentry *);
+
+static struct file_operations efs_dir_ops = {
+ NULL,
+ NULL,
+ NULL,
+ efs_readdir,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct inode_operations efs_dir_in_ops = {
+ &efs_dir_ops,
+ NULL,
+ efs_lookup,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ efs_bmap,
+ NULL,
+ NULL
+};
+
+
+/* ----- efs_readdir -----
+ readdir inode operation:
+ read the next directory entry of a given dir file
+
+ inode - pointer to inode struct of directory
+ filp - pointer to file struct of directory inode
+ dirent - pointer to dirent struct that has to be filled
+ filldir - function to store values in the directory
+
+ return - 0 ok, <0 error
+*/
+static int efs_readdir(struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct efs_inode_info *ini = (struct efs_inode_info *)&inode->u.efs_i;
+ struct buffer_head *bh;
+ __u8 *rawdirblk;
+ __u32 iteminode;
+ __u16 namelen;
+ __u8 *nameptr;
+ __u32 numitems;
+ __u16 itemnum;
+ __u32 block;
+ __u16 rawdepos;
+
+ /* some checks */
+ if(!inode || !inode->i_sb || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+
+ /* Warnings */
+ if(ini->tot!=1)
+ printk("efs_readdir: More than one extent!\n");
+ if(inode->i_size & (EFS_BLOCK_SIZE-1))
+ printk("efs_readdir: dirsize != blocksize*n\n");
+
+ /* f_pos contains: dirblock<<BLOCK_SIZE | # of item in dirblock */
+ block = filp->f_pos >> EFS_BLOCK_SIZE_BITS;
+ itemnum = filp->f_pos & 0xff;
+
+ /* We found the last entry -> ready */
+ if(block == (inode->i_size>>EFS_BLOCK_SIZE_BITS))
+ return 0;
+
+ /* get disc block number from dir block num: 0..i_size/BLOCK_SIZE */
+ bh = bread(inode->i_dev,efs_bmap(inode,block),EFS_BLOCK_SIZE);
+ if(!bh) return 0;
+
+ /* dirblock */
+ rawdirblk = (__u8 *)bh->b_data;
+ /* number of entries stored in this dirblock */
+ numitems = rawdirblk[EFS_DB_ENTRIES];
+ /* offset in block of #off diritem */
+ rawdepos = (__u16)rawdirblk[EFS_DB_FIRST+itemnum]<<1;
+
+ /* diritem first contains the inode number, the namelen and the name */
+ iteminode = ConvertLong(rawdirblk,rawdepos);
+ namelen = (__u16)rawdirblk[rawdepos+EFS_DI_NAMELEN];
+ nameptr = rawdirblk + rawdepos + EFS_DI_NAME;
+
+#ifdef DEBUG
+ printk("efs: dir #%d @ %0#3x - inode %lx %s namelen %u\n",itemnum,rawdepos,iteminode,nameptr,namelen);
+#endif
+
+ /* copy filename and data in direntry */
+ filldir(dirent,nameptr,namelen,filp->f_pos,iteminode);
+
+ brelse(bh);
+
+ /* store pos of next item */
+ itemnum++;
+ if(itemnum==numitems) {
+ itemnum = 0;
+ block++;
+ }
+ filp->f_pos = (block<<EFS_BLOCK_SIZE_BITS) | itemnum;
+ UPDATE_ATIME(inode);
+
+ return 0;
+}
+
diff --git a/fs/efs/file.c b/fs/efs/file.c
new file mode 100644
index 000000000..4c04616bb
--- /dev/null
+++ b/fs/efs/file.c
@@ -0,0 +1,218 @@
+/* file.c
+
+ read files on EFS filesystems
+ now replaced by generic functions of the kernel:
+ leaves only mapping of file block number -> disk block number in this file
+
+ (C)95,96 Christian Vogelgsang
+*/
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/efs_fs.h>
+#include <linux/efs_fs_i.h>
+#include <linux/efs_fs_sb.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>
+
+static struct file_operations efs_file_ops = {
+ NULL,
+ generic_file_read,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ generic_file_mmap,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct inode_operations efs_file_in_ops = {
+ &efs_file_ops,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ generic_readpage,
+ NULL,
+ efs_bmap,
+ NULL,
+ NULL
+};
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#define CHECK(num) \
+ eblk = ini->extents[num].ex_bytes[0]; \
+ epos = ini->extents[num].ex_bytes[1] & 0xffffff; \
+ elen = ini->extents[num].ex_bytes[1] >> 24; \
+ if((blk >= epos)&&(blk < (epos+elen))) \
+ result = (blk - epos) + eblk + sbi->fs_start;
+
+
+/* ----- efs_getblknum -----
+ find the disc block number for a given logical file block number
+
+ in - inode of file
+ blk - logical file block number
+
+ return - 0 on error, or unmapped block number
+*/
+static __u32 efs_getblk(struct inode *in,__u32 blk)
+{
+ struct efs_sb_info *sbi = &in->i_sb->u.efs_sb;
+ struct efs_inode_info *ini = &in->u.efs_i;
+ struct buffer_head *bh;
+
+ __u32 result = 0;
+ __u32 eblk,epos,elen;
+ int num,extnum,readahead;
+ __u32 extblk;
+ __u16 extoff,pos,cur,tot;
+ union efs_extent *ptr;
+
+
+ /* first check the current extend */
+ cur = ini->cur;
+ tot = ini->tot;
+ CHECK(cur)
+ if(result)
+ return result;
+
+ /* if only one extent exists and we are here the test failed */
+ if(tot==1) {
+ printk("efs: bmap failed on one extent!\n");
+ return 0;
+ }
+
+ /* check the stored extents in the inode */
+ num = MIN(tot,EFS_MAX_EXTENTS);
+ for(pos=0;pos<num;pos++) {
+ /* don't check the current again! */
+ if(pos==cur)
+ continue;
+
+ CHECK(pos)
+ if(result) {
+ ini->cur = pos;
+ return result;
+ }
+ }
+
+ /* If the inode has only direct extents,
+ the above tests must have found the block's extend! */
+ if(tot<=EFS_MAX_EXTENTS) {
+ printk("efs: bmap failed for direct extents!\n");
+ return 0;
+ }
+
+ /* --- search in the indirect extensions list blocks --- */
+#ifdef DEBUG
+ printk("efs - indirect search for %lu\n",blk);
+#endif
+
+ /* calculate block and offset for begin of extent descr and read it */
+ extblk = ini->extblk;
+ extoff = 0;
+ bh = bread(in->i_dev,extblk,EFS_BLOCK_SIZE);
+ if(!bh) {
+ printk("efs: read error in indirect extents\n");
+ return 0;
+ }
+ ptr = (union efs_extent *)bh->b_data;
+
+ pos = 0; /* number of extend store in the inode */
+ extnum = 0; /* count the extents in the indirect blocks */
+ readahead = 10; /* number of extents to read ahead */
+ while(1) {
+
+ /* skip last current extent store in the inode */
+ if(pos==cur) pos++;
+
+ /* read new extent in inode buffer */
+ ini->extents[pos].ex_bytes[0] = efs_swab32(ptr[pos].ex_bytes[0]);
+ ini->extents[pos].ex_bytes[1] = efs_swab32(ptr[pos].ex_bytes[1]);
+
+ /* we must still search */
+ if(!result) {
+ CHECK(pos)
+ if(result)
+ ini->cur = pos;
+ }
+ /* we found it already and read ahead */
+ else {
+ readahead--;
+ if(!readahead)
+ break;
+ }
+
+ /* next storage place */
+ pos++;
+ extnum++;
+
+ /* last extent checked -> finished */
+ if(extnum==tot) {
+ if(!result)
+ printk("efs: bmap on indirect failed!\n");
+ break;
+ }
+
+ extoff += 8;
+ /* need new block */
+ if(extoff==EFS_BLOCK_SIZE) {
+ extoff = 0;
+ extblk++;
+
+ brelse(bh);
+ bh = bread(in->i_dev,extblk,EFS_BLOCK_SIZE);
+ if(!bh) {
+ printk("efs: read error in indirect extents\n");
+ return 0;
+ }
+ ptr = (union efs_extent *)bh->b_data;
+ }
+ }
+ brelse(bh);
+
+ return result;
+}
+
+
+/* ----- efs_bmap -----
+ bmap: map a file block number to a device block number
+
+ in - inode owning the block
+ block - block number
+
+ return - disk block
+*/
+int efs_bmap(struct inode *in, int block)
+{
+ /* quickly reject invalid block numbers */
+ if(block<0) {
+#ifdef DEBUG
+ printk("efs_bmap: block < 0\n");
+#endif
+ return 0;
+ }
+ /* since the kernel wants to read a full page of data, i.e. 8 blocks
+ we must check if the block number is not too large */
+ if(block>((in->i_size-1)>>EFS_BLOCK_SIZE_BITS)) {
+#ifdef DEBUG
+ printk("efs_bmap: block %d > max %d == %d\n",
+ block,in->i_size>>EFS_BLOCK_SIZE_BITS,in->i_blocks);
+#endif
+ return 0;
+ }
+
+ return efs_getblk(in,block);
+}
diff --git a/fs/efs/inode.c b/fs/efs/inode.c
new file mode 100644
index 000000000..b84231cd5
--- /dev/null
+++ b/fs/efs/inode.c
@@ -0,0 +1,496 @@
+/* inode.c
+ *
+ * Inode and Superblock handling for the EFS filesystem
+ *
+ * (C) 1995,96 Christian Vogelgsang
+ */
+
+#include <linux/module.h>
+#include <linux/init.h> /* __initfunc */
+#include <linux/fs.h>
+#include <linux/efs_fs.h>
+#include <linux/efs_fs_i.h>
+#include <linux/efs_fs_sb.h>
+#include <linux/locks.h>
+
+#include <asm/uaccess.h>
+
+/* ----- Define the operations for the Superblock ----- */
+void efs_read_inode(struct inode *);
+void efs_put_super(struct super_block *);
+int efs_statfs(struct super_block *, struct statfs *,int );
+
+static struct super_operations efs_sops = {
+ efs_read_inode,
+ NULL, /* write_inode */
+ NULL, /* put_inode */
+ NULL, /* delete_inode */
+ NULL, /* notify_change */
+ efs_put_super,
+ NULL, /* write_super */
+ efs_statfs,
+ NULL
+};
+
+
+/* ----- Conversion utilities -----
+ Get 16- and 32-bit unsigned values in big-endian format from a byte buffer
+*/
+__u32 ConvertLong(__u8 *buf, int offset)
+{
+ return *((__u32 *)(buf + offset));
+ /* return (__u32)buf[offset+3] |
+ ((__u32)buf[offset+2])<<8 |
+ ((__u32)buf[offset+1])<<16 |
+ ((__u32)buf[offset])<<24;
+ */
+}
+
+__u16 ConvertShort(__u8 *buf, int offset)
+{
+ return *((__u16 *)(buf + offset));
+ /*
+ return (__u16)buf[offset+1] |
+ ((__u16)buf[offset])<<8;
+ */
+}
+
+
+/* ----- Install/Remove Module -----
+ These procedures are used to install/remove our filesystem
+ module in the linux kernel
+*/
+
+/* describe filesystem */
+struct super_block *efs_read_super(struct super_block *s, void *d, int sil);
+
+static struct file_system_type efs_fs_type = {
+ "efs",
+ FS_REQUIRES_DEV,
+ efs_read_super,
+ NULL
+};
+
+__initfunc(int init_efs_fs(void)) {
+ return register_filesystem(&efs_fs_type);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+/* install module */
+int init_module(void)
+{
+ return init_efs_fs();
+}
+
+/* remove module */
+void cleanup_module(void)
+{
+ unregister_filesystem(&efs_fs_type);
+}
+#endif /* MODULE */
+
+#ifdef DEBUG_EFS
+void efs_dump_super(struct efs_super_block *sb) {
+ printk("efs_super_block @ 0x%p:\n", sb);
+ printk("size: %0#8lX firstcg: %0#8lX\n", sb->s_size, sb->s_firstcg);
+ printk("cgfsize: %0#8lX cgisize: %0#8hX\n", sb->s_cgfsize,sb->s_cgisize);
+ printk("sectors: %0#8hX heads: %0#8hX\n", sb->s_sectors, sb->s_heads);
+ printk("ncg: %0#8hX dirty: %0#8hX\n", sb->s_ncg, sb->s_dirty);
+ printk("time: %0#8lX magic: %0#8lX\n", sb->s_time, sb->s_magic);
+ printk("fname: %.6s fpack: %.6s\n", sb->s_fname, sb->s_fpack);
+ printk("bmsize: %0#8lX tfree: %0#8lX\n", sb->s_bmsize, sb->s_tfree);
+ printk("tinode: %0#8lX bmblock: %0#8lX\n", sb->s_tinode, sb->s_bmblock);
+ printk("replsb: %0#8lX lastiall: %0#8lX\n", sb->s_replsb,
+ sb->s_lastialloc);
+ printk("checksum: %0#8lX\n", sb->s_checksum);
+}
+
+void efs_dump_inode(struct efs_disk_inode *di) {
+ printk("efs_disk_inode @ 0x%p: ", di);
+ printk("[%o %hd %hd %u %u %u %u %u #ext=%hd %u %u]\n",
+ di->di_mode, di->di_nlink, di->di_uid, di->di_gid, di->di_size,
+ di->di_atime, di->di_mtime, di->di_ctime, di->di_gen,
+ di->di_numextents, di->di_version);
+}
+#endif
+
+/* ----- efs_checkVolDesc -----
+ Analyse the first block of a CD and check
+ if it's a valid efs volume descriptor
+
+ blk - buffer with the data of first block
+ silent - 0 -> verbose
+
+ return : 0 - error ; >0 - start block of filesystem
+*/
+#if 0
+static __u32 efs_checkVolDesc(__u8 *blk,int silent)
+{
+ __u32 magic;
+ __u8 *ptr;
+ __u8 name[10];
+ __u32 pos,len;
+ int i;
+
+ /* is the magic cookie here? */
+ magic = ConvertLong(blk,0);
+ if(magic!=0x0be5a941) {
+ printk("EFS: no magic on first block\n");
+ return 0;
+ }
+
+ /* Walk through the entries of the VD */
+ /* Quite useless, but gives nice output ;-) */
+ ptr = blk + EFS_VD_ENTRYFIRST;
+ name[8] = 0;
+ while(*ptr) {
+ for(i=0;i<8;i++)
+ name[i] = ptr[i];
+
+ /* start and length of entry */
+ pos = ConvertLong(ptr,EFS_VD_ENTRYPOS);
+ len = ConvertLong(ptr,EFS_VD_ENTRYLEN);
+
+ if(!silent)
+ printk("EFS: VolDesc: %8s blk: %08lx len: %08lx\n",
+ name,pos,len);
+
+ ptr+=EFS_VD_ENTRYSIZE;
+ }
+
+ pos = ConvertLong(blk,EFS_VD_FS_START);
+ printk("EFS: FS start: %08lx\n",pos);
+ return pos;
+}
+#endif
+
+/* ----- efs_checkSuper -----
+ Check if the given block is a valid EFS-superblock
+
+ sbi - my EFS superblock info
+ block - block that must be examined
+
+ return - 0 ok, -1 error
+*/
+static int efs_verify_super(struct efs_super_block *sb,
+ struct efs_sb_info *sbi,
+ int silent)
+{
+ __u32 magic;
+
+ magic = sb->s_magic;
+ /* check if the magic cookie is here */
+ if((magic!=EFS_MAGIC1)&&(magic!=EFS_MAGIC2)) {
+ printk("EFS: magic %#X doesn't match %#X or %#X!\n",
+ magic, EFS_MAGIC1, EFS_MAGIC2);
+ return -1;
+ }
+
+ /* XXX should check csum */
+
+ sbi->total_blocks = sb->s_size;
+ sbi->first_block = sb->s_firstcg;
+ sbi->group_size = sb->s_cgfsize;
+ sbi->inode_blocks = sb->s_cgisize;
+ sbi->total_groups = sb->s_ncg;
+
+ return 0;
+}
+
+
+/* ----- efs_read_super -----
+ read_super: if the fs gets mounted this procedure is called to
+ check if the filesystem is valid and to fill the superblock struct
+
+ s - superblock struct
+ d - options for fs (unused)
+ sil - flag to be silent
+
+ return - filled s struct or NULL on error
+ */
+
+struct super_block *efs_read_super(struct super_block *s, void *d, int silent)
+{
+ struct buffer_head *bh;
+ struct efs_sb_info *sb_info = (struct efs_sb_info *)&s->u.efs_sb;
+ struct efs_super_block *es;
+ int error = 0;
+ int dev = s->s_dev;
+ struct inode *root_inode = NULL;
+
+ MOD_INC_USE_COUNT;
+
+ /* say hello to my log file! */
+#ifdef EFS_DEBUG
+ if(!silent)
+ printk("EFS: --- Filesystem ---\n");
+#endif
+ /* set blocksize to 512 */
+ set_blocksize(dev, EFS_BLOCK_SIZE);
+
+ lock_super(s);
+
+#if 0
+ /* Read first block of CD: the Volume Descriptor */
+ bh = bread(dev, 0, EFS_BLOCK_SIZE);
+ if(bh) {
+ sb_info->fs_start = efs_checkVolDesc((__u8 *)bh->b_data,silent);
+ if(sb_info->fs_start==0) {
+ printk("EFS: failed checking Volume Descriptor\n");
+ /* error++; */
+ }
+ brelse(bh);
+ } else {
+ printk("EFS: cannot read the first block\n");
+ error++;
+ }
+#endif 0
+
+ /* Read the Superblock */
+ if(!error) {
+#ifdef DEBUG_EFS
+ if (!silent)
+ printk("EFS: reading superblock.\n");
+#endif
+ bh = bread(dev, EFS_BLK_SUPER, EFS_BLOCK_SIZE );
+ if(bh) {
+ es = (struct efs_super_block *)(bh->b_data);
+#ifdef DEBUG_EFS
+ if(!silent)
+ efs_dump_super(es);
+#endif
+ if(efs_verify_super(es, sb_info, silent)) {
+ printk("EFS: failed checking Superblock\n");
+ error++;
+ }
+ brelse(bh);
+ } else {
+ printk("EFS: cannot read the superblock\n");
+ error++;
+ }
+ }
+
+ if(!error) {
+ s->s_blocksize = EFS_BLOCK_SIZE;
+ s->s_blocksize_bits = EFS_BLOCK_SIZE_BITS;
+
+ s->s_magic = EFS_SUPER_MAGIC;
+ s->s_flags = MS_RDONLY;
+ s->s_op = &efs_sops;
+ s->s_dev = dev;
+ root_inode = iget(s, EFS_ROOT_INODE);
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root) {
+ error++;
+ printk("EFS: couldn't allocate root inode!\n");
+ }
+ }
+
+ unlock_super(s);
+
+ if(check_disk_change(s->s_dev)) {
+ printk("EFS: Device changed!\n");
+ error++;
+ }
+
+ /* We found errors -> say goodbye! */
+ if(error) {
+ s->s_dev = 0;
+ d_delete(s->s_root); /* XXX is this enough? */
+ printk("EFS: init failed with %d errors\n", error);
+ brelse(bh);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ return s;
+}
+
+
+/* ----- efs_put_super -----
+ put_super: remove the filesystem and the module use count
+
+ s - superblock
+*/
+void efs_put_super(struct super_block *s)
+{
+ lock_super(s);
+ s->s_dev = 0;
+ unlock_super(s);
+ MOD_DEC_USE_COUNT;
+}
+
+
+/* ----- efs_statfs -----
+ statfs: get informatio on the filesystem
+
+ s - superblock of fs
+ buf - statfs struct that has to be filled
+*/
+int efs_statfs(struct super_block *s, struct statfs *buf,int bufsize)
+{
+ struct efs_sb_info *sbi = (struct efs_sb_info *)&s->u.generic_sbp;
+ struct statfs tmp;
+
+ tmp.f_type = EFS_SUPER_MAGIC;
+ tmp.f_bsize = EFS_BLOCK_SIZE;
+ tmp.f_blocks = sbi->total_blocks;
+ tmp.f_bfree = 0;
+ tmp.f_bavail = 0;
+ tmp.f_files = 100; /* don't know how to calculate the correct value */
+ tmp.f_ffree = 0;
+ tmp.f_namelen = NAME_MAX;
+
+ return copy_to_user(buf,&tmp,bufsize) ? -EFAULT : 0;
+}
+
+
+/* ----- efs_read_inode -----
+ read an inode specified by in->i_ino from disk, fill the inode
+ structure and install the correct handler for the file type
+
+ in - inode struct
+*/
+void efs_read_inode(struct inode *in)
+{
+ struct buffer_head *bh;
+ struct efs_sb_info *sbi = (struct efs_sb_info *)&in->i_sb->u.generic_sbp;
+ __u32 blk,off;
+ int error = 0;
+
+ /* Calc the discblock and the offset for inode (4 Nodes fit in one block) */
+ blk = in->i_ino >> 2;
+ blk = sbi->fs_start + sbi->first_block +
+ (sbi->group_size * (blk / sbi->inode_blocks)) +
+ (blk % sbi->inode_blocks);
+ off = (in->i_ino&3)<<7;
+
+ /* Read the block with the inode from disk */
+#ifdef DEBUG_EFS
+ printk("EFS: looking for inode %#xl\n", in->i_ino);
+#endif
+ bh = bread(in->i_dev,blk,EFS_BLOCK_SIZE);
+ if(bh) {
+
+ struct efs_disk_inode *di = (struct efs_disk_inode *)(bh->b_data + off);
+ __u16 numext;
+ struct efs_inode_info *ini = &in->u.efs_i;
+ __u32 rdev;
+ int i;
+
+ /* fill in standard inode infos */
+ in->i_mtime = efs_swab32(di->di_mtime);
+ in->i_ctime = efs_swab32(di->di_ctime);
+ in->i_atime = efs_swab32(di->di_atime);
+ in->i_size = efs_swab32(di->di_size);
+ in->i_nlink = efs_swab16(di->di_nlink);
+ in->i_uid = efs_swab16(di->di_uid);
+ in->i_gid = efs_swab16(di->di_gid);
+ in->i_mode = efs_swab16(di->di_mode);
+
+ /* Special files store their rdev value where the extends of
+ a regular file are found */
+ /* rdev = ConvertLong(rawnode,EFS_IN_EXTENTS);*/
+ /* XXX this isn't right */
+ rdev = efs_swab32(*(__u32 *)&di->di_u.di_dev);
+
+ /* -----------------------------------------------------------------
+ The following values are stored in my private part of the Inode.
+ They are necessary for further operations with the file */
+
+ /* get the number of extends the inode posseses */
+ numext = efs_swab16(di->di_numextents);
+
+ /* if this inode has more than EFS_MAX_EXTENDS then the extends are
+ stored not directly in the inode but indirect on an extra block.
+ The address of the extends-block is stored in the inode */
+ if(numext>EFS_MAX_EXTENTS) {
+ struct buffer_head *bh2;
+ printk("EFS: inode %#Xl has > EFS_MAX_EXTENTS (%ld)\n",
+ in->i_ino, numext);
+
+ /* Store the discblock and offset of extend-list in Inode info */
+ ini->extblk = sbi->fs_start + efs_swab32((__u32)(di->di_u.di_extents));
+
+ /* read INI_MAX_EXT extents from the indirect block */
+ bh2 = bread(in->i_dev,ini->extblk,EFS_BLOCK_SIZE);
+ if(bh2) {
+ union efs_extent *ptr = (union efs_extent *)bh2->b_data;
+ for(i=0;i<EFS_MAX_EXTENTS;i++) {
+ ini->extents[i].ex_bytes[0] = efs_swab32(ptr[i].ex_bytes[0]);
+ ini->extents[i].ex_bytes[1] = efs_swab32(ptr[i].ex_bytes[1]);
+ }
+ brelse(bh2);
+ } else
+ printk("efs: failed reading indirect extends!\n");
+
+ } else {
+#ifdef DEBUG_EFS
+ printk("EFS: inode %#Xl is direct (woohoo!)\n",
+ in->i_ino);
+#endif
+ /* The extends are found in the inode block */
+ ini->extblk = blk;
+
+ /* copy extends directly from rawinode */
+ for(i=0;i<numext;i++) {
+ ini->extents[i].ex_bytes[0] = efs_swab32(di->di_u.di_extents[i].ex_bytes[0]);
+ ini->extents[i].ex_bytes[1] = efs_swab32(di->di_u.di_extents[i].ex_bytes[1]);
+ }
+ }
+ ini->tot = numext;
+ ini->cur = 0;
+
+ brelse(bh);
+
+#ifdef DEBUG_EFS
+ printk("%lx inode: blk %lx numext %x\n",in->i_ino,ini->extblk,numext);
+ efs_dump_inode(di);
+#endif
+
+ /* Install the filetype Handler */
+ switch(in->i_mode & S_IFMT) {
+ case S_IFDIR:
+ in->i_op = &efs_dir_in_ops;
+ break;
+ case S_IFREG:
+ in->i_op = &efs_file_in_ops;
+ break;
+ case S_IFLNK:
+ in->i_op = &efs_symlink_in_ops;
+ break;
+ case S_IFCHR:
+ in->i_rdev = rdev;
+ in->i_op = &chrdev_inode_operations;
+ break;
+ case S_IFBLK:
+ in->i_rdev = rdev;
+ in->i_op = &blkdev_inode_operations;
+ break;
+ case S_IFIFO:
+ init_fifo(in);
+ break;
+ default:
+ printk("EFS: Unsupported inode Mode %o\n",(unsigned int)(in->i_mode));
+ error++;
+ break;
+ }
+
+ } else {
+ printk("EFS: Inode: failed bread!\n");
+ error++;
+ }
+
+ /* failed inode */
+ if(error) {
+ printk("EFS: read inode failed with %d errors\n",error);
+ in->i_mtime = in->i_atime = in->i_ctime = 0;
+ in->i_size = 0;
+ in->i_nlink = 1;
+ in->i_uid = in->i_gid = 0;
+ in->i_mode = S_IFREG;
+ in->i_op = NULL;
+ }
+}
diff --git a/fs/efs/namei.c b/fs/efs/namei.c
new file mode 100644
index 000000000..82086accd
--- /dev/null
+++ b/fs/efs/namei.c
@@ -0,0 +1,143 @@
+/* namei.c
+
+ name lookup for EFS filesystem
+
+ (C)95,96 Christian Vogelgsang
+*/
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/efs_fs.h>
+#include <linux/efs_fs_i.h>
+#include <linux/efs_fs_sb.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>
+
+/* ----- efs_find_entry -----
+ search a raw efs dir entry for the given name
+
+ dir - inode of directory
+ oname - name to search for
+ onamelen- length of name
+
+ return - inode number of the found entry or 0 on error
+*/
+static struct buffer_head * efs_find_entry(struct inode *dir,
+ const char *oname,
+ int onamelen,
+ struct efs_dir_entry *res_dir)
+{
+ struct efs_inode_info *ini = &dir->u.efs_i;
+ struct buffer_head *bh = NULL;
+ __u32 inode;
+ __u16 i;
+ __u8 *name;
+ __u16 namelen;
+ __u32 blknum,b;
+ struct efs_dirblk *db;
+
+ /* Warnings */
+ if(ini->tot!=1)
+ printk("efs_find: More than one extent!\n");
+ if(dir->i_size & (EFS_BLOCK_SIZE-1))
+ printk("efs_find: dirsize != blocklen * n\n");
+
+ /* Search in every dirblock */
+ inode = 0;
+ blknum = dir->i_size >> EFS_BLOCK_SIZE_BITS;
+#ifdef DEBUG_EFS
+ printk("EFS: directory with inode %#xd has %d blocks\n",
+ dir->i_ino, blknum);
+#endif
+ for(b=0;b<blknum;b++) {
+ int db_offset;
+#ifdef DEBUG_EFS
+ printk("EFS: trying block %d\n", b);
+#endif
+ /* Read a raw dirblock */
+ bh = bread(dir->i_dev,efs_bmap(dir,b),EFS_BLOCK_SIZE);
+ if(!bh) {
+ printk("EFS: efs_bmap returned NULL!\n");
+ return 0;
+ }
+
+ db = (struct efs_dirblk *)bh->b_data;
+ if (db->db_magic != EFS_DIRBLK_MAGIC) {
+ printk("EFS: dirblk has bad magic (%#xl)!\n",
+ db->db_magic);
+ return NULL;
+ }
+#ifdef DEBUG_EFS
+ printk("EFS: db %d has %d entries, starting at offset %#x\n",
+ b, db->db_slots, (__u16)db->db_firstused << 1);
+#endif
+ for(i = 0 ; i < db->db_slots ; i++) {
+ struct efs_dent * de;
+ int entry_inode;
+ db_offset = ((__u16)db->db_space[i] << 1)
+ - EFS_DIRBLK_HEADERSIZE;
+ de = (struct efs_dent *)(&db->db_space[db_offset]);
+
+ /* inode, namelen and name of direntry */
+ entry_inode = efs_swab32(de->ud_inum.l);
+ namelen = de->d_namelen;
+ name = de->d_name;
+#ifdef DEBUG_EFS
+ printk("EFS: entry %d @ %#x has inode %#x, %s/%d\n",
+ i, db_offset, entry_inode, name, namelen);
+#endif
+ /* we found the name! */
+ if((namelen==onamelen)&&
+ (!memcmp(oname,name,onamelen))) {
+ res_dir->inode = entry_inode;
+#ifdef DEBUG_EFS
+ printk("EFS: found inode %d\n",
+ entry_inode);
+#endif
+ return bh;
+ }
+ }
+#ifdef DEBUG_EFS
+ printk("EFS: efs_find_entry didn't find inode for \"%s\"!\n",
+ oname);
+#endif
+ return NULL;
+ }
+ printk("EFS: Falling off the end of efs_find_entry! Bogus!\n");
+ return NULL;
+ /* not reached */
+}
+
+
+/* ----- efs_lookup -----
+ lookup inode operation:
+ check if a given name is in the dir directory
+
+ dir - pointer to inode of directory
+ name - name we must search for
+ len - length of name
+ result - pointer to inode struct if we found it or NULL on error
+
+ return - 0 everything is ok or <0 on error
+*/
+int efs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct buffer_head *bh;
+ struct efs_dir_entry de;
+
+ bh = efs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
+ if (bh) {
+ int ino = de.inode;
+ brelse(bh);
+ inode = iget(dir->i_sb, ino);
+
+ if (!inode)
+ return -EACCES;
+ }
+
+ d_add(dentry, inode);
+ return 0;
+}
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
new file mode 100644
index 000000000..6197bb6f3
--- /dev/null
+++ b/fs/efs/symlink.c
@@ -0,0 +1,136 @@
+/* symlink.c
+ *
+ * Symbolic link handling for EFS
+ *
+ * (C)1995,96 Christian Vogelgsang
+ *
+ * Based on the symlink.c from minix-fs by Linus
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/efs_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+static int efs_readlink(struct inode *, char *, int);
+static struct dentry * efs_follow_link(struct inode *, struct dentry *);
+
+struct inode_operations efs_symlink_in_ops = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ efs_readlink, /* readlink */
+ efs_follow_link, /* follow_link */
+ NULL,
+ NULL,
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+/* ----- efs_getlinktarget -----
+ read the target of the link from the data zone of the file
+*/
+static char *efs_getlinktarget(struct inode *in)
+{
+ struct buffer_head * bh;
+ char *name;
+ __u32 size = in->i_size;
+ __u32 block;
+
+ /* link data longer than 1024 not supported */
+ if(size>2*EFS_BLOCK_SIZE) {
+ printk("efs_getlinktarget: name too long: %lu\n",in->i_size);
+ return NULL;
+ }
+
+ /* get some memory from the kernel to store the name */
+ name = kmalloc(size+1,GFP_KERNEL);
+ if(!name) return NULL;
+
+ /* read first 512 bytes of target */
+ block = efs_bmap(in,0);
+ bh = bread(in->i_dev,block,EFS_BLOCK_SIZE);
+ if(!bh) {
+ kfree(name);
+ return NULL;
+ }
+ memcpy(name,bh->b_data,(size>EFS_BLOCK_SIZE)?EFS_BLOCK_SIZE:size);
+ brelse(bh);
+
+ /* if the linktarget is long, read the next block */
+ if(size>EFS_BLOCK_SIZE) {
+ bh = bread(in->i_dev,block+1,EFS_BLOCK_SIZE);
+ if(!bh) {
+ kfree(name);
+ return NULL;
+ }
+ memcpy(name+EFS_BLOCK_SIZE,bh->b_data,size-EFS_BLOCK_SIZE);
+ brelse(bh);
+ }
+
+ /* terminate string and return it */
+ name[size]=0;
+ return name;
+}
+
+
+/* ----- efs_follow_link -----
+ get the inode of the link target
+*/
+static struct dentry * efs_follow_link(struct inode * dir, struct dentry *base)
+{
+ struct buffer_head * bh;
+ __u32 block;
+
+ block = efs_bmap(dir,0);
+ bh = bread(dir->i_dev,block,EFS_BLOCK_SIZE);
+ if (!bh) {
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ UPDATE_ATIME(dir);
+ base = lookup_dentry(bh->b_data, base, 1);
+ brelse(bh);
+ return base;
+}
+
+/* ----- efs_readlink -----
+ read the target of a link and return the name
+*/
+static int efs_readlink(struct inode * dir, char * buffer, int buflen)
+{
+ int i;
+ char c;
+ struct buffer_head * bh;
+ __u32 block;
+
+ block = efs_bmap(dir,0);
+ if (buflen > 1023)
+ buflen = 1023;
+ bh = bread(dir->i_dev,block,EFS_BLOCK_SIZE);
+ if (!bh)
+ return 0;
+
+ /* copy the link target to the given buffer */
+ i = 0;
+ while (i<buflen && (c = bh->b_data[i])) {
+ i++;
+ put_user(c,buffer++);
+ }
+
+ brelse(bh);
+ return i;
+}