diff options
Diffstat (limited to 'fs/efs/inode.c')
-rw-r--r-- | fs/efs/inode.c | 549 |
1 files changed, 223 insertions, 326 deletions
diff --git a/fs/efs/inode.c b/fs/efs/inode.c index f00b06520..9e7da7934 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -1,348 +1,245 @@ /* - * linux/fs/efs/inode.c + * inode.c * - * Copyright (C) 1998 Mike Shaver + * Copyright (c) 1999 Al Smith * - * Portions derived from work (C) 1995,1996 Christian Vogelgsang. - * ``Inspired by'' fs/minix/inode.c. + * Portions derived from work (c) 1995,1996 Christian Vogelgsang, + * and from work (c) 1998 Mike Shaver. */ -#include <linux/module.h> /* module apparatus */ -#include <linux/init.h> /* __initfunc */ -#include <linux/efs_fs.h> -#include <linux/locks.h> -#include <asm/uaccess.h> - -#define COPY_EXTENT(from, to) \ -{ \ - to.ex_bytes[0] = efs_swab32(from.ex_bytes[0]); \ - to.ex_bytes[1] = efs_swab32(from.ex_bytes[1]); \ -} - -void -efs_put_super(struct super_block *sb) -{ - MOD_DEC_USE_COUNT; -} +#include <linux/efs.h> + +void efs_read_inode(struct inode *in) { + int i, extents, inode_index; + dev_t device; + struct buffer_head *bh; + struct efs_spb *sbp = (struct efs_spb *)&in->i_sb->u.generic_sbp; + struct efs_in_info *ini = (struct efs_in_info *)&in->u.generic_ip; + efs_block_t block, offset; + struct efs_dinode *efs_inode; + + /* + ** EFS layout: + ** + ** | cylinder group | cylinder group | cylinder group ..etc + ** |inodes|data |inodes|data |inodes|data ..etc + ** + ** work out the inode block index, (considering initially that the + ** inodes are stored as consecutive blocks). then work out the block + ** number of that inode given the above layout, and finally the + ** offset of the inode within that block. + */ + + /* four inodes are stored in one block */ + inode_index = in->i_ino / (EFS_BLOCKSIZE / sizeof(struct efs_dinode)); + + block = sbp->fs_start + sbp->first_block + + (sbp->group_size * (inode_index / sbp->inode_blocks)) + + (inode_index % sbp->inode_blocks); + + offset = (in->i_ino % (EFS_BLOCKSIZE / sizeof(struct efs_dinode))) << 7; + + bh = bread(in->i_dev, block, EFS_BLOCKSIZE); + if (!bh) { + printk("EFS: bread() failed at block %d\n", block); + goto read_inode_error; + } -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, /* remount */ -}; - -static const char * -efs_checkroot(struct super_block *sb, struct inode *dir) -{ - struct buffer_head *bh; - - if (!S_ISDIR(dir->i_mode)) - return "root directory is not a directory"; - - bh = bread(dir->i_dev, efs_bmap(dir, 0), EFS_BLOCK_SIZE); - if (!bh) - return "unable to read root directory"; - - /* XXX check sanity of root directory */ + efs_inode = (struct efs_dinode *) (bh->b_data + offset); - brelse(bh); - return NULL; -} + /* fill in standard inode infos */ + in->i_mode = be16_to_cpu(efs_inode->di_mode); + in->i_nlink = be16_to_cpu(efs_inode->di_nlink); + in->i_uid = be16_to_cpu(efs_inode->di_uid); + in->i_gid = be16_to_cpu(efs_inode->di_gid); + in->i_size = be32_to_cpu(efs_inode->di_size); + in->i_atime = be32_to_cpu(efs_inode->di_atime); + in->i_mtime = be32_to_cpu(efs_inode->di_mtime); + in->i_ctime = be32_to_cpu(efs_inode->di_ctime); + + /* this is last valid block in the file */ + in->i_blocks = ((in->i_size - 1) >> EFS_BLOCKSIZE_BITS); + + device = be32_to_cpu(efs_inode->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 extents for this object */ + extents = be16_to_cpu(efs_inode->di_numextents); + + /* copy the first 12 extents directly from the inode */ + for(i = 0; i < EFS_DIRECTEXTENTS; i++) { + /* ick. horrible (ab)use of union */ + ini->extents[i].u1.l = be32_to_cpu(efs_inode->di_u.di_extents[i].u1.l); + ini->extents[i].u2.l = be32_to_cpu(efs_inode->di_u.di_extents[i].u2.l); + if (i < extents && ini->extents[i].u1.s.ex_magic != 0) { + printk("EFS: extent %d has bad magic number in inode %lu\n", i, in->i_ino); + brelse(bh); + goto read_inode_error; + } + } + ini->numextents = extents; + ini->lastextent = 0; + + brelse(bh); + +#ifdef DEBUG + printk("EFS: efs_read_inode(): inode %lu, extents %d\n", + in->i_ino, extents); +#endif -struct super_block * -efs_read_super(struct super_block *s, void *data, int silent) -{ - struct buffer_head *bh; - struct efs_disk_sb *efs_sb; - struct efs_sb_info *sbi; - kdev_t dev = s->s_dev; - const char *errmsg = "default error message"; - struct inode *root; - __u32 magic; - - DB(("read_super on dev %s\n", kdevname(dev))); - MOD_INC_USE_COUNT; - - lock_super(s); - set_blocksize(dev, EFS_BLOCK_SIZE); - - /* - * XXXshaver - * If this is a CDROM, then there's a volume descriptor at block - * EFS_BLK_VOLDESC. What's there if it's just a disk partition? - */ - - bh = bread(dev, EFS_BLK_SUPER, EFS_BLOCK_SIZE); - if (!bh) - goto out_bad_sb; - - efs_sb = (struct efs_disk_sb *)bh->b_data; - sbi = &s->u.efs_sb; - sbi->total_blocks = efs_sb->s_size; - sbi->first_block = efs_sb->s_firstcg; - sbi->group_size = efs_sb->s_cgfsize; - sbi->inode_blocks = efs_sb->s_cgisize; - sbi->total_groups = efs_sb->s_ncg; - magic = efs_sb->s_magic; - brelse(bh); - - if (magic == EFS_MAGIC1 || magic == EFS_MAGIC2) { - DB(("EFS: valid superblock magic\n")); - } else { - goto out_no_fs; - } - - if (efs_sb->s_dirty != EFS_CLEAN) { - switch(efs_sb->s_dirty) { - case EFS_ACTIVE: - errmsg = "Partition was not unmounted properly, but is clean"; - break; - case EFS_ACTIVEDIRT: - errmsg = "Partition was mounted dirty and not cleanly unmounted"; - break; - case EFS_DIRTY: - errmsg = "Partition was not umounted properly, and is dirty"; - break; - default: - errmsg = "unknown!\n"; - break; + /* Install the filetype Handler */ + switch (in->i_mode & S_IFMT) { + case S_IFDIR: + in->i_op = &efs_dir_inode_operations; + break; + case S_IFREG: + in->i_op = &efs_file_inode_operations; + break; + case S_IFLNK: + in->i_op = &efs_symlink_inode_operations; + break; + case S_IFCHR: + in->i_rdev = device; + in->i_op = &chrdev_inode_operations; + break; + case S_IFBLK: + in->i_rdev = device; + in->i_op = &blkdev_inode_operations; + break; + case S_IFIFO: + init_fifo(in); + break; + default: + printk("EFS: unsupported inode mode %o\n",in->i_mode); + goto read_inode_error; + break; } - if (!silent) - printk("EFS: ERROR: cleanliness is %#04x: %s\n", efs_sb->s_dirty, - errmsg); - goto out_unlock; - } - - s->s_blocksize = EFS_BLOCK_SIZE; - s->s_blocksize_bits = EFS_BLOCK_SIZE_BITS; - s->s_magic = EFS_SUPER_MAGIC; - s->s_op = &efs_sops; - DB(("getting root inode (%d)\n", EFS_ROOT_INODE)); - root = iget(s, EFS_ROOT_INODE); - - if (!root->i_size) - goto out_bad_root; - DB(("checking root inode\n")); - errmsg = efs_checkroot(s, root); - if (errmsg) - goto out_bad_root; - - DB(("root inode OK\n")); - - s->s_root = d_alloc_root(root, NULL); - if (!s->s_root) - goto out_iput; - - /* we only do RO right now */ - if (!(s->s_flags & MS_RDONLY)) { - if (!silent) - printk("EFS: forcing read-only: RW access not supported\n"); - s->s_flags |= MS_RDONLY; - } - - unlock_super(s); - return s; - - /* error-handling exit paths */ - out_bad_root: - if (!silent && errmsg) - printk("EFS: bad_root ERROR: %s\n", errmsg); - - out_iput: - iput(root); - brelse(bh); - goto out_unlock; - - out_no_fs: - printk("EFS: ERROR: bad magic\n"); - goto out_unlock; - - out_bad_sb: - printk("EFS: unable to read superblock\n"); - - out_unlock: - s->s_dev = 0; - unlock_super(s); - MOD_DEC_USE_COUNT; - return NULL; -} -int -efs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) -{ - struct statfs tmp; - - DB(("statfs\n")); - tmp.f_type = sb->s_magic; - tmp.f_bsize = sb->s_blocksize; - tmp.f_blocks = sb->u.efs_sb.total_blocks; - tmp.f_bfree = 0; - tmp.f_bavail = 0; - tmp.f_files = 0; /* XXX? */ - tmp.f_ffree = 0; - tmp.f_namelen = NAME_MAX; - return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; + return; + +read_inode_error: + printk("EFS: failed to read inode %lu\n", in->i_ino); + in->i_mode = S_IFREG; + in->i_atime = 0; + in->i_ctime = 0; + in->i_mtime = 0; + in->i_nlink = 1; + in->i_size = 0; + in->i_blocks = 0; + in->i_uid = 0; + in->i_gid = 0; + in->i_op = NULL; + + return; } -void -efs_read_inode(struct inode *in) -{ - struct efs_sb_info *efs_sb = &in->i_sb->u.efs_sb; - struct buffer_head *bh; - struct efs_disk_inode *di; - struct efs_inode_info *ini = &in->u.efs_i; - int block, ino = in->i_ino, offset; - __u16 numext; - __u32 rdev; - - DB(("read_inode\n")); - - /* - * Calculate the disk block and offset for the inode. - * There are 4 inodes per block. - */ - block = ino / EFS_INODES_PER_BLOCK; - - /* - * Inodes are stored at the beginning of every cylinder group. - * - * We find the block containing the inode like so: - * - block is set above to the ``logical'' block number - * - first_block is the start of the FS - * - (block / inode_blocks) is the cylinder group that the inode is in. - * - (block % inode_blocks) is the block offset within the cg - * - */ - block = efs_sb->first_block + - (efs_sb->group_size * (block / efs_sb->inode_blocks)) + - (block % efs_sb->inode_blocks); - - /* find the offset */ - offset = (ino % EFS_INODES_PER_BLOCK) << 7; - - DB(("EFS: looking for inode #%xl in blk %d offset %d\n", - ino, block, offset)); - - bh = bread(in->i_dev, block, EFS_BLOCK_SIZE); - - if (!bh) { - printk("EFS: failed to bread blk #%xl for inode %#xl\n", block, ino); - goto error; - } - - di = (struct efs_disk_inode *)(bh->b_data + offset); - - /* standard inode info */ - 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); - - DB(("INODE %ld: mt %ld ct %ld at %ld sz %ld nl %ld uid %ld gid %ld mode %lo\n", - in->i_ino, - in->i_mtime, in->i_ctime, in->i_atime, in->i_size, in->i_nlink, - in->i_uid, in->i_gid, in->i_mode)); - - rdev = efs_swab32(*(__u32 *) &di->di_u.di_dev); - numext = efs_swab16(di->di_numextents); - - if (numext > EFS_MAX_EXTENTS) { - DB(("EFS: inode %#0x is indirect (%d)\n", ino, numext)); +static inline efs_block_t +efs_extent_check(efs_extent *ptr, efs_block_t block, struct efs_spb *sbi, struct efs_in_info *ini) { + efs_block_t start; + efs_block_t length; + efs_block_t offset; /* - * OPT: copy the first 10 extents in here? - */ - } else { - int i; - - DB(("EFS: inode %#lx is direct (%d). Happy day!\n", in->i_ino, - numext)); - ini->extblk = block; - - /* copy extents into inode_info */ - for (i = 0; i < numext; i++) { - COPY_EXTENT(di->di_u.di_extents[i], ini->extents[i]); + ** given an extent and a logical block within a file, + ** can this block be found within this extent ? + */ + start = ptr->u1.s.ex_bn; + length = ptr->u2.s.ex_length; + offset = ptr->u2.s.ex_offset; + + if ((block >= offset) && (block < offset+length)) { + return(sbi->fs_start + start + block - offset); + } else { + return 0; } - - } - ini->tot = numext; - ini->cur = 0; - brelse(bh); - - if (S_ISDIR(in->i_mode)) - in->i_op = &efs_dir_inode_operations; - else if (S_ISREG(in->i_mode)) - in->i_op = &efs_file_inode_operations; - else if (S_ISLNK(in->i_mode)) - in->i_op = &efs_symlink_inode_operations; - else if (S_ISCHR(in->i_mode)) { - in->i_rdev = rdev; - in->i_op = &chrdev_inode_operations; - } else if (S_ISBLK(in->i_mode)) { - in->i_rdev = rdev; - in->i_op = &blkdev_inode_operations; - } else if (S_ISFIFO(in->i_mode)) - init_fifo(in); - else { - printk("EFS: ERROR: unsupported inode mode %#lo (dir is %#lo) =? %d\n", - (in->i_mode & S_IFMT), (long)S_IFDIR, - in->i_mode & S_IFMT == S_IFDIR); - goto error; - } - - return; - - error: - DB(("ERROR: INODE %ld: mt %ld ct %ld at %ld sz %ld nl %ld uid %ld " - "gid %ld mode %lo\n", in->i_ino, - in->i_mtime, in->i_ctime, in->i_atime, in->i_size, in->i_nlink, - in->i_uid, in->i_gid, in->i_mode)); - 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; } -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); -} +/* find the disk block number for a given logical file block number */ -#ifdef MODULE -EXPORT_NO_SYMBOLS; +efs_block_t efs_read_block(struct inode *inode, efs_block_t block) { + struct efs_spb *sbi = (struct efs_spb *) &inode->i_sb->u.generic_sbp; + struct efs_in_info *ini = (struct efs_in_info *) &inode->u.generic_ip; + struct buffer_head *bh; -int -init_module(void) -{ - DB(("loading EFS module\n")); - return init_efs_fs(); -} + efs_block_t result = 0; + int indirexts, indirext, imagic; + efs_block_t istart, iblock, ilen; + int i, last, total, checked; + efs_extent *exts; + efs_extent tmp; -void -cleanup_module(void) -{ - DB(("removing EFS module\n")); - unregister_filesystem(&efs_fs_type); -} + last = ini->lastextent; + total = ini->numextents; + if (total <= EFS_DIRECTEXTENTS) { + /* first check the last extent we returned */ + if ((result = efs_extent_check(&ini->extents[last], block, sbi, ini))) + return result; + + /* if we only have one extent then nothing can be found */ + if (total == 1) { + printk("EFS: read_block() failed to map (1 extent)\n"); + return 0; + } + + /* check the stored extents in the inode */ + /* start with next extent and check forwards */ + for(i = 0; i < total - 1; i++) { + if ((result = efs_extent_check(&ini->extents[(last + i) % total], block, sbi, ini))) { + ini->lastextent = i; + return result; + } + } + + printk("EFS: read_block() failed to map for direct extents\n"); + return 0; + } + +#ifdef DEBUG + printk("EFS: indirect search for logical block %u\n", block); #endif + indirexts = ini->extents[0].u2.s.ex_offset; + checked = 0; + + for(indirext = 0; indirext < indirexts; indirext++) { + imagic = ini->extents[indirext].u1.s.ex_magic; + istart = ini->extents[indirext].u1.s.ex_bn + sbi->fs_start; + ilen = ini->extents[indirext].u2.s.ex_length; + + for(iblock = istart; iblock < istart + ilen; iblock++) { + bh = bread(inode->i_dev, iblock, EFS_BLOCKSIZE); + if (!bh) { + printk("EFS: bread() failed at block %d\n", block); + return 0; + } + + exts = (struct extent *) bh->b_data; + for(i = 0; i < EFS_BLOCKSIZE / sizeof(efs_extent) && checked < total; i++, checked++) { + tmp.u1.l = be32_to_cpu(exts[i].u1.l); + tmp.u2.l = be32_to_cpu(exts[i].u2.l); + + if (tmp.u1.s.ex_magic != 0) { + printk("EFS: extent %d has bad magic number in block %d\n", i, iblock); + brelse(bh); + return 0; + } + + if ((result = efs_extent_check(&tmp, block, sbi, ini))) { + brelse(bh); + return result; + } + } + brelse(bh); + /* shouldn't need this if the FS is consistent */ + if (checked == total) { + printk("EFS: unable to map (checked all extents)\n"); + return 0; + } + } + } + printk("EFS: unable to map (fell out of loop)\n"); + return 0; +} + |