diff options
author | Mike Shaver <shaver@ingenia.com> | 1997-09-16 15:05:24 +0000 |
---|---|---|
committer | Mike Shaver <shaver@ingenia.com> | 1997-09-16 15:05:24 +0000 |
commit | 4e589e524e0c4c23fd8c6217696bd9983dc8a721 (patch) | |
tree | eecd52c3e576218b98aa69d57cbaaa030e5a4f29 /fs/efs | |
parent | 6ce1685bcf39c7a589acdeb9639e71c4dde91ab0 (diff) |
EFS integration (read-only, slight problem with symlinks).
Diffstat (limited to 'fs/efs')
-rw-r--r-- | fs/efs/Makefile | 17 | ||||
-rw-r--r-- | fs/efs/dir.c | 132 | ||||
-rw-r--r-- | fs/efs/file.c | 218 | ||||
-rw-r--r-- | fs/efs/inode.c | 496 | ||||
-rw-r--r-- | fs/efs/namei.c | 143 | ||||
-rw-r--r-- | fs/efs/symlink.c | 136 |
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; +} |