diff options
Diffstat (limited to 'fs/efs/inode.c')
-rw-r--r-- | fs/efs/inode.c | 741 |
1 files changed, 291 insertions, 450 deletions
diff --git a/fs/efs/inode.c b/fs/efs/inode.c index 7928e45b4..735d86d99 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -1,498 +1,339 @@ -/* inode.c - * - * Inode and Superblock handling for the EFS filesystem +/* + * linux/fs/efs/inode.c * - * (C) 1995,96 Christian Vogelgsang + * Copyright (C) 1998 Mike Shaver + * + * Portions derived from work (C) 1995,1996 Christian Vogelgsang. + * ``Inspired by'' fs/minix/inode.c. */ - -#include <linux/module.h> + +#include <linux/module.h> /* module apparatus */ #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; - */ +#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]); \ } -__u16 ConvertShort(__u8 *buf, int offset) +void +efs_put_super(struct super_block *sb) { - return *((__u16 *)(buf + offset)); - /* - return (__u16)buf[offset+1] | - ((__u16)buf[offset])<<8; - */ + DB(("efs_put_super ... ")); + lock_super(sb); + DB(("locked ... ")); + sb->s_dev = 0; + unlock_super(sb); + DB(("unlocked ... ")); + MOD_DEC_USE_COUNT; + DB(("MOD_DEC_USE_COUNT\n")); } +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 */ +}; -/* ----- Install/Remove Module ----- - These procedures are used to install/remove our filesystem - module in the linux kernel -*/ +static const char * +efs_checkroot(struct super_block *sb, struct inode *dir) +{ + struct buffer_head *bh; -/* describe filesystem */ -struct super_block *efs_read_super(struct super_block *s, void *d, int sil); + if (!S_ISDIR(dir->i_mode)) + return "root directory is not a directory"; -static struct file_system_type efs_fs_type = { - "efs", - FS_REQUIRES_DEV, - efs_read_super, - NULL -}; + bh = bread(dir->i_dev, efs_bmap(dir, 0), EFS_BLOCK_SIZE); + if (!bh) + return "unable to read root directory"; -__initfunc(int init_efs_fs(void)) { - return register_filesystem(&efs_fs_type); + /* XXX check sanity of root directory */ + + brelse(bh); + return NULL; } -#ifdef MODULE -EXPORT_NO_SYMBOLS; - -/* install module */ -int init_module(void) +struct super_block * +efs_read_super(struct super_block *s, void *data, int silent) { - return init_efs_fs(); + struct buffer_head *bh; + struct efs_disk_sb *efs_sb; + struct efs_sb_info *sbi; + kdev_t dev = s->s_dev; + const char *errmsg; + struct inode *root; + __u32 magic; + + DB(("read_super\n")); + 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; + } + if (!silent) + printk("EFS: ERROR: %s\n", 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; + root = iget(s, EFS_ROOT_INODE); + + errmsg = efs_checkroot(s, root); + if (errmsg) + goto out_bad_root; + + 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) + printk("EFS: ERROR: %s\n", errmsg); + goto out_unlock; + + 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; } -/* remove module */ -void cleanup_module(void) +int +efs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - unregister_filesystem(&efs_fs_type); + 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; } -#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) +void +efs_read_inode(struct inode *in) { - __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); + 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")); + + /* + * 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); - ptr+=EFS_VD_ENTRYSIZE; - } + /* find the offset */ + offset = (ino % EFS_INODES_PER_BLOCK) << 7; - pos = ConvertLong(blk,EFS_VD_FS_START); - printk("EFS: FS start: %08lx\n",pos); - return pos; -} -#endif + DB(("EFS: looking for inode #%xl\n", ino)); -/* ----- 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; - } + bh = bread(in->i_dev, block, EFS_BLOCK_SIZE); - /* 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; -} + 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); -/* ----- 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 - */ + /* 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); -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 + rdev = efs_swab32(*(__u32 *) &di->di_u.di_dev); + numext = efs_swab16(di->di_numextents); - /* 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"); - } - } + if (numext > EFS_MAX_EXTENTS) { + DB(("EFS: inode #%lx is indirect (%d)\n", ino, numext)); + + /* + * OPT: copy the first 10 extents in here? + */ + } else { + int i; - unlock_super(s); - - if(check_disk_change(s->s_dev)) { - printk("EFS: Device changed!\n"); - error++; + DB(("EFS: inode %#lx is direct. Happy day!\n", in->i_ino)); + 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]); } - /* 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; + } + ini->tot = numext; + ini->cur = 0; + brelse(bh); + + 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 = 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: ERROR: unsupported inode mode %lo\n", + (in->i_mode & S_IFMT)); + goto error; + } + + return; + + 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; } +static struct file_system_type efs_fs_type = { + "efs", + FS_REQUIRES_DEV, + efs_read_super, + NULL +}; -/* ----- efs_put_super ----- - put_super: remove the filesystem and the module use count - - s - superblock -*/ -void efs_put_super(struct super_block *s) +__initfunc(int +init_efs_fs(void)) { - lock_super(s); - s->s_dev = 0; - unlock_super(s); - MOD_DEC_USE_COUNT; + return register_filesystem(&efs_fs_type); } +#ifdef MODULE +EXPORT_NO_SYMBOLS; -/* ----- 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) +int +init_module(void) { - 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; + return init_efs_fs(); } - -/* ----- 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) +void +cleanup_module(void) { - 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 #%lx has > EFS_MAX_EXTENTS (%d)\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 */ - printk("EFS: "); - 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 extents!\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; - } + unregister_filesystem(&efs_fs_type); } + +#endif |