description:	handle limited hard blocksizes in EFS (512, 1024, 2048 bytes)
changelog:	convert all reads from logical to physical sector numbers &
		add a buffer_head offset to get back to the logical sectors
maintainer:	<abandoned>
product_versions: Linux 2.6.0-test8
patch_name:	efs_blocksizes.patch
patch_version:	2003-10-21.21:29:22
author:		Randy.Dunlap <rddunlap@osdl.org>

diffstat:=
 fs/efs/dir.c              |   15 +++++++++------
 fs/efs/file.c             |    4 ++--
 fs/efs/inode.c            |   36 +++++++++++++++++++++++++++++-------
 fs/efs/namei.c            |   12 ++++++------
 fs/efs/super.c            |   34 +++++++++++++++++++++-------------
 fs/efs/symlink.c          |   13 ++++++++-----
 include/linux/efs_fs.h    |    2 ++
 include/linux/efs_fs_sb.h |    1 +
 8 files changed, 78 insertions(+), 39 deletions(-)

diff -Naur ./fs/efs/dir.c~blocksize ./fs/efs/dir.c
--- ./fs/efs/dir.c~blocksize	2003-10-17 14:43:20.000000000 -0700
+++ ./fs/efs/dir.c	2003-10-21 20:28:34.000000000 -0700
@@ -19,10 +19,12 @@
 	.lookup		= efs_lookup,
 };
 
-static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir) {
+static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
 	struct inode *inode = filp->f_dentry->d_inode;
 	struct buffer_head *bh;
-
+	struct buffer_head *bh_log;
+	unsigned long		bh_log_offset;
 	struct efs_dir		*dirblock;
 	struct efs_dentry	*dirslot;
 	efs_ino_t		inodenum;
@@ -44,14 +46,15 @@
 	/* look at all blocks */
 	while (block < inode->i_blocks) {
 		/* read the dir block */
-		bh = sb_bread(inode->i_sb, efs_bmap(inode, block));
+		bh = efs_bread(inode->i_sb, efs_bmap(inode, block), &bh_log_offset);
 
 		if (!bh) {
 			printk(KERN_ERR "EFS: readdir(): failed to read dir block %d\n", block);
 			break;
 		}
 
-		dirblock = (struct efs_dir *) bh->b_data; 
+		bh_log = (void *)bh + bh_log_offset;
+		dirblock = (struct efs_dir *) bh_log->b_data; 
 
 		if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) {
 			printk(KERN_ERR "EFS: readdir(): invalid directory block\n");
@@ -65,7 +68,8 @@
 				continue;
 			}
 
-			dirslot  = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot));
+			dirslot  = (struct efs_dentry *) (((char *) bh_log->b_data)
+					+ EFS_SLOTAT(dirblock, slot));
 
 			inodenum = be32_to_cpu(dirslot->inode);
 			namelen  = dirslot->namelen;
@@ -110,4 +114,3 @@
 	unlock_kernel();
 	return 0;
 }
-
diff -Naur ./fs/efs/file.c~blocksize ./fs/efs/file.c
--- ./fs/efs/file.c~blocksize	2003-10-17 14:43:15.000000000 -0700
+++ ./fs/efs/file.c	2003-10-21 20:34:45.000000000 -0700
@@ -35,8 +35,8 @@
 	return 0;
 }
 
-int efs_bmap(struct inode *inode, efs_block_t block) {
-
+int efs_bmap(struct inode *inode, efs_block_t block)
+{
 	if (block < 0) {
 		printk(KERN_WARNING "EFS: bmap(): block < 0\n");
 		return 0;
diff -Naur ./fs/efs/inode.c~blocksize ./fs/efs/inode.c
--- ./fs/efs/inode.c~blocksize	2003-10-17 14:43:03.000000000 -0700
+++ ./fs/efs/inode.c	2003-10-21 21:27:36.000000000 -0700
@@ -52,7 +52,8 @@
 	int i, inode_index;
 	dev_t device;
 	u32 rdev;
-	struct buffer_head *bh;
+	struct buffer_head *bh, *bh_log;
+	unsigned long bh_log_offset;
 	struct efs_sb_info    *sb = SUPER_INFO(inode->i_sb);
 	struct efs_inode_info *in = INODE_INFO(inode);
 	efs_block_t block, offset;
@@ -81,13 +82,14 @@
 			(EFS_BLOCKSIZE / sizeof(struct efs_dinode))) *
 		sizeof(struct efs_dinode);
 
-	bh = sb_bread(inode->i_sb, block);
+	bh = efs_bread(inode->i_sb, block, &bh_log_offset);
 	if (!bh) {
 		printk(KERN_WARNING "EFS: bread() failed at block %d\n", block);
 		goto read_inode_error;
 	}
 
-	efs_inode = (struct efs_dinode *) (bh->b_data + offset);
+	bh_log = (void *)bh + bh_log_offset;
+	efs_inode = (struct efs_dinode *) (bh_log->b_data + offset);
     
 	inode->i_mode  = be16_to_cpu(efs_inode->di_mode);
 	inode->i_nlink = be16_to_cpu(efs_inode->di_nlink);
@@ -191,11 +193,13 @@
 	}
 }
 
-efs_block_t efs_map_block(struct inode *inode, efs_block_t block) {
+efs_block_t efs_map_block(struct inode *inode, efs_block_t block)
+{
 	struct efs_sb_info    *sb = SUPER_INFO(inode->i_sb);
 	struct efs_inode_info *in = INODE_INFO(inode);
 	struct buffer_head    *bh = NULL;
-
+	struct buffer_head    *bh_log = NULL;
+	unsigned long bh_log_offset;
 	int cur, last, first = 1;
 	int ibase, ioffset, dirext, direxts, indext, indexts;
 	efs_block_t iblock, result = 0, lastblock = 0;
@@ -271,11 +275,12 @@
 		if (first || lastblock != iblock) {
 			if (bh) brelse(bh);
 
-			bh = sb_bread(inode->i_sb, iblock);
+			bh = efs_bread(inode->i_sb, iblock, &bh_log_offset);
 			if (!bh) {
 				printk(KERN_ERR "EFS: bread() failed at block %d\n", iblock);
 				return 0;
 			}
+			bh_log = (void *)bh + bh_log_offset;
 #ifdef DEBUG
 			printk(KERN_DEBUG "EFS: map_block(): read indirect extent block %d\n", iblock);
 #endif
@@ -283,7 +288,7 @@
 			lastblock = iblock;
 		}
 
-		exts = (efs_extent *) bh->b_data;
+		exts = (efs_extent *) bh_log->b_data;
 
 		extent_copy(&(exts[ioffset]), &ext);
 
@@ -304,4 +309,21 @@
 	return 0;
 }  
 
+struct buffer_head *efs_bread(struct super_block *sb,
+				sector_t block, unsigned long *bh_offset)
+{
+	struct efs_sb_info *sinfo = SUPER_INFO(sb);
+	unsigned blocking = sinfo->blocking;
+	sector_t phys_sector;
+
+	if (blocking == 1) {
+		*bh_offset = 0;
+		return sb_bread(sb, block);
+	}
+
+	*bh_offset = (block % blocking) * EFS_BLOCKSIZE;
+	phys_sector = block / blocking;
+	return sb_bread(sb, phys_sector);
+}
+
 MODULE_LICENSE("GPL");
diff -Naur ./fs/efs/namei.c~blocksize ./fs/efs/namei.c
--- ./fs/efs/namei.c~blocksize	2003-10-17 14:43:20.000000000 -0700
+++ ./fs/efs/namei.c	2003-10-21 20:28:31.000000000 -0700
@@ -12,8 +12,8 @@
 #include <linux/smp_lock.h>
 
 static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) {
-	struct buffer_head *bh;
-
+	struct buffer_head *bh, *bh_log;
+	unsigned long		bh_log_offset;
 	int			slot, namelen;
 	char			*nameptr;
 	struct efs_dir		*dirblock;
@@ -26,13 +26,14 @@
 
 	for(block = 0; block < inode->i_blocks; block++) {
 
-		bh = sb_bread(inode->i_sb, efs_bmap(inode, block));
+		bh = efs_bread(inode->i_sb, efs_bmap(inode, block), &bh_log_offset);
 		if (!bh) {
 			printk(KERN_ERR "EFS: find_entry(): failed to read dir block %d\n", block);
 			return 0;
 		}
     
-		dirblock = (struct efs_dir *) bh->b_data;
+		bh_log = (void *)bh + bh_log_offset;
+		dirblock = (struct efs_dir *) bh_log->b_data;
 
 		if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) {
 			printk(KERN_ERR "EFS: find_entry(): invalid directory block\n");
@@ -41,7 +42,7 @@
 		}
 
 		for(slot = 0; slot < dirblock->slots; slot++) {
-			dirslot  = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot));
+			dirslot  = (struct efs_dentry *) (((char *) bh_log->b_data) + EFS_SLOTAT(dirblock, slot));
 
 			namelen  = dirslot->namelen;
 			nameptr  = dirslot->name;
@@ -74,4 +75,3 @@
 	d_add(dentry, inode);
 	return NULL;
 }
-
diff -Naur ./fs/efs/super.c~blocksize ./fs/efs/super.c
--- ./fs/efs/super.c~blocksize	2003-10-17 14:43:25.000000000 -0700
+++ ./fs/efs/super.c	2003-10-21 21:09:02.000000000 -0700
@@ -209,7 +209,9 @@
 int efs_fill_super(struct super_block *s, void *d, int silent)
 {
 	struct efs_sb_info *sb;
-	struct buffer_head *bh;
+	struct buffer_head *bh, *bh_log;
+	unsigned long bh_log_offset;
+	unsigned long blocksize;
 
  	sb = kmalloc(sizeof(struct efs_sb_info), GFP_KERNEL);
 	if (!sb)
@@ -217,40 +219,47 @@
 	s->s_fs_info = sb;
 	memset(sb, 0, sizeof(struct efs_sb_info));
  
-	s->s_magic		= EFS_SUPER_MAGIC;
-	if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) {
-		printk(KERN_ERR "EFS: device does not support %d byte blocks\n",
-			EFS_BLOCKSIZE);
-		goto out_no_fs_ul;
+	s->s_magic   = EFS_SUPER_MAGIC;
+	sb->blocking = 1;
+	for (blocksize = EFS_BLOCKSIZE; blocksize <= 4 * EFS_BLOCKSIZE;
+			blocksize += blocksize, sb->blocking += sb->blocking) {
+		if (!sb_set_blocksize(s, blocksize))
+			printk(KERN_ERR "EFS: device does not support %ld byte blocks\n",
+				blocksize);
+		else
+			goto efs_have_blocksize;
 	}
-  
-	/* read the vh (volume header) block */
-	bh = sb_bread(s, 0);
+	goto out_no_fs_ul;
 
+efs_have_blocksize:
+	/* read the vh (volume header) block */
+	bh = efs_bread(s, 0, &bh_log_offset);
 	if (!bh) {
 		printk(KERN_ERR "EFS: cannot read volume header\n");
 		goto out_no_fs_ul;
 	}
+	bh_log = (void *)bh + bh_log_offset;
 
 	/*
 	 * if this returns zero then we didn't find any partition table.
 	 * this isn't (yet) an error - just assume for the moment that
 	 * the device is valid and go on to search for a superblock.
 	 */
-	sb->fs_start = efs_validate_vh((struct volume_header *) bh->b_data);
+	sb->fs_start = efs_validate_vh((struct volume_header *) bh_log->b_data);
 	brelse(bh);
 
 	if (sb->fs_start == -1) {
 		goto out_no_fs_ul;
 	}
 
-	bh = sb_bread(s, sb->fs_start + EFS_SUPER);
+	bh = efs_bread(s, sb->fs_start + EFS_SUPER, &bh_log_offset);
 	if (!bh) {
 		printk(KERN_ERR "EFS: cannot read superblock\n");
 		goto out_no_fs_ul;
 	}
+	bh_log = (void *)bh + bh_log_offset;
 		
-	if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
+	if (efs_validate_super(sb, (struct efs_super *) bh_log->b_data)) {
 #ifdef DEBUG
 		printk(KERN_WARNING "EFS: invalid superblock at block %u\n", sb->fs_start + EFS_SUPER);
 #endif
@@ -301,4 +310,3 @@
 
 	return 0;
 }
-
diff -Naur ./fs/efs/symlink.c~blocksize ./fs/efs/symlink.c
--- ./fs/efs/symlink.c~blocksize	2003-10-17 14:43:16.000000000 -0700
+++ ./fs/efs/symlink.c	2003-10-21 20:39:05.000000000 -0700
@@ -15,7 +15,8 @@
 static int efs_symlink_readpage(struct file *file, struct page *page)
 {
 	char *link = kmap(page);
-	struct buffer_head * bh;
+	struct buffer_head * bh, *bh_log;
+	unsigned long bh_log_offset;
 	struct inode * inode = page->mapping->host;
 	efs_block_t size = inode->i_size;
 	int err;
@@ -27,16 +28,18 @@
 	lock_kernel();
 	/* read first 512 bytes of link target */
 	err = -EIO;
-	bh = sb_bread(inode->i_sb, efs_bmap(inode, 0));
+	bh = efs_bread(inode->i_sb, efs_bmap(inode, 0), &bh_log_offset);
 	if (!bh)
 		goto fail;
-	memcpy(link, bh->b_data, (size > EFS_BLOCKSIZE) ? EFS_BLOCKSIZE : size);
+	bh_log = (void *)bh + bh_log_offset;
+	memcpy(link, bh_log->b_data, (size > EFS_BLOCKSIZE) ? EFS_BLOCKSIZE : size);
 	brelse(bh);
 	if (size > EFS_BLOCKSIZE) {
-		bh = sb_bread(inode->i_sb, efs_bmap(inode, 1));
+		bh = efs_bread(inode->i_sb, efs_bmap(inode, 1), &bh_log_offset);
 		if (!bh)
 			goto fail;
-		memcpy(link + EFS_BLOCKSIZE, bh->b_data, size - EFS_BLOCKSIZE);
+		bh_log = (void *)bh + bh_log_offset;
+		memcpy(link + EFS_BLOCKSIZE, bh_log->b_data, size - EFS_BLOCKSIZE);
 		brelse(bh);
 	}
 	link[size] = '\0';
diff -Naur ./include/linux/efs_fs.h~blocksize ./include/linux/efs_fs.h
--- ./include/linux/efs_fs.h~blocksize	2003-10-17 14:43:04.000000000 -0700
+++ ./include/linux/efs_fs.h	2003-10-21 20:08:39.000000000 -0700
@@ -48,5 +48,7 @@
 
 extern struct dentry *efs_lookup(struct inode *, struct dentry *, struct nameidata *);
 extern int efs_bmap(struct inode *, int);
+extern struct buffer_head *efs_bread(struct super_block *sb,
+				sector_t block, unsigned long *bh_offset);
 
 #endif /* __EFS_FS_H__ */
diff -Naur ./include/linux/efs_fs_sb.h~blocksize ./include/linux/efs_fs_sb.h
--- ./include/linux/efs_fs_sb.h~blocksize	2003-10-17 14:43:20.000000000 -0700
+++ ./include/linux/efs_fs_sb.h	2003-10-21 20:48:22.000000000 -0700
@@ -56,6 +56,7 @@
 	int32_t	inode_free;	/* # of free inodes */
 	short	inode_blocks;	/* # of blocks used for inodes in every grp */
 	short	total_groups;	/* # of groups */
+	int32_t	blocking;	/* number of logical blocks per physical block */
 };
 
 #endif /* __EFS_FS_SB_H__ */
