diff mbox series

[1/1] package/genext2fs: bump to version 1.5.0

Message ID 20210414214439.1381969-1-fontaine.fabrice@gmail.com
State Accepted
Headers show
Series [1/1] package/genext2fs: bump to version 1.5.0 | expand

Commit Message

Fabrice Fontaine April 14, 2021, 9:44 p.m. UTC
- Retrieve latest version from github
- Drop patch (already in version)

Fixes:
 - https://bugs.buildroot.org/show_bug.cgi?id=13741

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
---
 ...0001-update-genext2fs.c-to-rev-1.118.patch | 2971 -----------------
 package/genext2fs/genext2fs.hash              |    2 +-
 package/genext2fs/genext2fs.mk                |    6 +-
 3 files changed, 5 insertions(+), 2974 deletions(-)
 delete mode 100644 package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch

Comments

Arnout Vandecappelle April 15, 2021, 7:20 p.m. UTC | #1
On 14/04/2021 23:44, Fabrice Fontaine wrote:
> - Retrieve latest version from github
> - Drop patch (already in version)
> 
> Fixes:
>  - https://bugs.buildroot.org/show_bug.cgi?id=13741
> 
> Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>


 Applied to master, thanks.

 I guess for stable, the patch [1] should be backported rather than bumping. But
is it worth spending effort on that for a borderline package like genext2fs?

 Regards,
 Arnout


[1]
https://github.com/bestouff/genext2fs/pull/14/commits/8e4b9ae6f051454953a80b8e64443bcea6c9cc46



> ---
>  ...0001-update-genext2fs.c-to-rev-1.118.patch | 2971 -----------------
>  package/genext2fs/genext2fs.hash              |    2 +-
>  package/genext2fs/genext2fs.mk                |    6 +-
>  3 files changed, 5 insertions(+), 2974 deletions(-)
>  delete mode 100644 package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch
> 
> diff --git a/package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch b/package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch
> deleted file mode 100644
> index 755ee9dee2..0000000000
> --- a/package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch
> +++ /dev/null
> @@ -1,2971 +0,0 @@
> -[PATCH] update genext2fs.c to CVS rev 1.118
> -
> -See http://genext2fs.cvs.sourceforge.net/viewvc/genext2fs/genext2fs/genext2fs.c?view=log
> -for details.
> -
> -Numerous bugfixes, large file and filesystem support, rev 1 filesystems,
> -volume id support, block size, ..
> -
> -Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
> ----
> - cache.h     |  128 ++++
> - genext2fs.c | 1870 ++++++++++++++++++++++++++++++++++++++++++------------------
> - list.h      |   78 ++
> - 3 files changed, 1527 insertions(+), 549 deletions(-)
> -
> -Index: genext2fs-1.4.1/genext2fs.c
> -===================================================================
> ---- genext2fs-1.4.1.orig/genext2fs.c
> -+++ genext2fs-1.4.1/genext2fs.c
> -@@ -53,6 +53,12 @@
> - // 			along with -q, -P, -U
> - 
> - 
> -+/*
> -+ * Allow fseeko/off_t to be 64-bit offsets to allow filesystems and
> -+ * individual files >2GB.
> -+ */
> -+#define _FILE_OFFSET_BITS 64
> -+
> - #include <config.h>
> - #include <stdio.h>
> - 
> -@@ -107,10 +113,8 @@
> - 
> - #if HAVE_DIRENT_H
> - # include <dirent.h>
> --# define NAMLEN(dirent) strlen((dirent)->d_name)
> - #else
> - # define dirent direct
> --# define NAMLEN(dirent) (dirent)->d_namlen
> - # if HAVE_SYS_NDIR_H
> - #  include <sys/ndir.h>
> - # endif
> -@@ -144,6 +148,8 @@
> - # include <limits.h>
> - #endif
> - 
> -+#include "cache.h"
> -+
> - struct stats {
> - 	unsigned long nblocks;
> - 	unsigned long ninodes;
> -@@ -151,13 +157,42 @@
> - 
> - // block size
> - 
> --#define BLOCKSIZE         1024
> -+static int blocksize = 1024;
> -+
> -+#define SUPERBLOCK_OFFSET	1024
> -+#define SUPERBLOCK_SIZE		1024
> -+
> -+#define BLOCKSIZE         blocksize
> - #define BLOCKS_PER_GROUP  8192
> - #define INODES_PER_GROUP  8192
> - /* Percentage of blocks that are reserved.*/
> - #define RESERVED_BLOCKS       5/100
> - #define MAX_RESERVED_BLOCKS  25/100
> - 
> -+/* The default value for s_creator_os. */
> -+#if defined(__linux__)    &&    defined(EXT2_OS_LINUX)
> -+#define CREATOR_OS EXT2_OS_LINUX
> -+#define CREATOR_OS_NAME "linux"
> -+#else
> -+#if defined(__GNU__)     &&     defined(EXT2_OS_HURD)
> -+#define CREATOR_OS EXT2_OS_HURD
> -+#define CREATOR_OS_NAME "hurd"
> -+#else
> -+#if defined(__FreeBSD__) &&     defined(EXT2_OS_FREEBSD)
> -+#define CREATOR_OS EXT2_OS_FREEBSD
> -+#define CREATOR_OS_NAME "freebsd"
> -+#else
> -+#if defined(LITES)         &&   defined(EXT2_OS_LITES)
> -+#define CREATOR_OS EXT2_OS_LITES
> -+#define CREATOR_OS_NAME "lites"
> -+#else
> -+#define CREATOR_OS EXT2_OS_LINUX /* by default */
> -+#define CREATOR_OS_NAME "linux"
> -+#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
> -+#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
> -+#endif /* defined(__GNU__)     && defined(EXT2_OS_HURD) */
> -+#endif /* defined(__linux__)   && defined(EXT2_OS_LINUX) */
> -+
> - 
> - // inode block size (why is it != BLOCKSIZE ?!?)
> - /* The field i_blocks in the ext2 inode stores the number of data blocks
> -@@ -190,6 +225,14 @@
> - #define EXT2_TIND_BLOCK    14                    // triple indirect block
> - #define EXT2_INIT_BLOCK    0xFFFFFFFF            // just initialized (not really a block address)
> - 
> -+// codes for operating systems
> -+
> -+#define EXT2_OS_LINUX           0
> -+#define EXT2_OS_HURD            1
> -+#define EXT2_OS_MASIX           2
> -+#define EXT2_OS_FREEBSD         3
> -+#define EXT2_OS_LITES           4
> -+
> - // end of a block walk
> - 
> - #define WALK_END           0xFFFFFFFE
> -@@ -227,44 +270,46 @@
> - #define FM_IWOTH   0000002	// write
> - #define FM_IXOTH   0000001	// execute
> - 
> --// options
> --
> --#define OP_HOLES     0x01       // make files with holes
> --
> - /* Defines for accessing group details */
> - 
> - // Number of groups in the filesystem
> - #define GRP_NBGROUPS(fs) \
> --	(((fs)->sb.s_blocks_count - fs->sb.s_first_data_block + \
> --	  (fs)->sb.s_blocks_per_group - 1) / (fs)->sb.s_blocks_per_group)
> -+	(((fs)->sb->s_blocks_count - fs->sb->s_first_data_block + \
> -+	  (fs)->sb->s_blocks_per_group - 1) / (fs)->sb->s_blocks_per_group)
> - 
> - // Get group block bitmap (bbm) given the group number
> --#define GRP_GET_GROUP_BBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_block_bitmap) )
> -+#define GRP_GET_GROUP_BBM(fs,grp,bi) (get_blk((fs),(grp)->bg_block_bitmap,(bi)))
> -+#define GRP_PUT_GROUP_BBM(bi) ( put_blk((bi)) )
> - 
> - // Get group inode bitmap (ibm) given the group number
> --#define GRP_GET_GROUP_IBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_inode_bitmap) )
> --		
> -+#define GRP_GET_GROUP_IBM(fs,grp,bi) (get_blk((fs), (grp)->bg_inode_bitmap,(bi)))
> -+#define GRP_PUT_GROUP_IBM(bi) ( put_blk((bi)) )
> -+
> - // Given an inode number find the group it belongs to
> --#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb.s_inodes_per_group)
> -+#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb->s_inodes_per_group)
> - 
> - //Given an inode number get the inode bitmap that covers it
> --#define GRP_GET_INODE_BITMAP(fs,nod) \
> --	( GRP_GET_GROUP_IBM((fs),GRP_GROUP_OF_INODE((fs),(nod))) )
> -+#define GRP_GET_INODE_BITMAP(fs,nod,bi,gi)				\
> -+	( GRP_GET_GROUP_IBM((fs),get_gd(fs,GRP_GROUP_OF_INODE((fs),(nod)),gi),bi) )
> -+#define GRP_PUT_INODE_BITMAP(bi,gi)		\
> -+	( GRP_PUT_GROUP_IBM((bi)),put_gd((gi)) )
> - 
> - //Given an inode number find its offset within the inode bitmap that covers it
> - #define GRP_IBM_OFFSET(fs,nod) \
> --	( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb.s_inodes_per_group )
> -+	( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb->s_inodes_per_group )
> - 
> - // Given a block number find the group it belongs to
> --#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb.s_blocks_per_group)
> -+#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb->s_blocks_per_group)
> - 	
> --//Given a block number get the block bitmap that covers it
> --#define GRP_GET_BLOCK_BITMAP(fs,blk) \
> --	( GRP_GET_GROUP_BBM((fs),GRP_GROUP_OF_BLOCK((fs),(blk))) )
> -+//Given a block number get/put the block bitmap that covers it
> -+#define GRP_GET_BLOCK_BITMAP(fs,blk,bi,gi)				\
> -+	( GRP_GET_GROUP_BBM((fs),get_gd(fs,GRP_GROUP_OF_BLOCK((fs),(blk)),(gi)),(bi)) )
> -+#define GRP_PUT_BLOCK_BITMAP(bi,gi)		\
> -+	( GRP_PUT_GROUP_BBM((bi)),put_gd((gi)) )
> - 
> - //Given a block number find its offset within the block bitmap that covers it
> - #define GRP_BBM_OFFSET(fs,blk) \
> --	( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb.s_blocks_per_group )
> -+	( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb->s_blocks_per_group )
> - 
> - 
> - // used types
> -@@ -286,7 +331,9 @@
> - // older solaris. Note that this is still not very portable, in that
> - // the return value cannot be trusted.
> - 
> --#if SCANF_CAN_MALLOC
> -+#if 0 // SCANF_CAN_MALLOC
> -+// C99 define "a" for floating point, so you can have runtime surprise
> -+// according the library versions
> - # define SCANF_PREFIX "a"
> - # define SCANF_STRING(s) (&s)
> - #else
> -@@ -430,6 +477,17 @@
> - 			((val<<8)&0xFF0000) | (val<<24));
> - }
> - 
> -+static inline int
> -+is_blk_empty(uint8 *b)
> -+{
> -+	uint32 i;
> -+	uint32 *v = (uint32 *) b;
> -+
> -+	for(i = 0; i < BLOCKSIZE / 4; i++)
> -+		if (*v++)
> -+			return 0;
> -+	return 1;
> -+}
> - 
> - // on-disk structures
> - // this trick makes me declare things only once
> -@@ -460,7 +518,22 @@
> - 	udecl32(s_creator_os)          /* Indicator of which OS created the filesystem */ \
> - 	udecl32(s_rev_level)           /* The revision level of the filesystem */ \
> - 	udecl16(s_def_resuid)          /* The default uid for reserved blocks */ \
> --	udecl16(s_def_resgid)          /* The default gid for reserved blocks */
> -+	udecl16(s_def_resgid)          /* The default gid for reserved blocks */ \
> -+	/* rev 1 version fields start here */ \
> -+	udecl32(s_first_ino) 		/* First non-reserved inode */	\
> -+	udecl16(s_inode_size) 		/* size of inode structure */	\
> -+	udecl16(s_block_group_nr) 	/* block group # of this superblock */ \
> -+	udecl32(s_feature_compat) 	/* compatible feature set */	\
> -+	udecl32(s_feature_incompat) 	/* incompatible feature set */	\
> -+	udecl32(s_feature_ro_compat) 	/* readonly-compatible feature set */ \
> -+	utdecl8(s_uuid,16)		/* 128-bit uuid for volume */	\
> -+	utdecl8(s_volume_name,16) 	/* volume name */		\
> -+	utdecl8(s_last_mounted,64) 	/* directory where last mounted */ \
> -+	udecl32(s_algorithm_usage_bitmap) /* For compression */
> -+
> -+#define EXT2_GOOD_OLD_FIRST_INO	11
> -+#define EXT2_GOOD_OLD_INODE_SIZE 128
> -+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
> - 
> - #define groupdescriptor_decl \
> - 	udecl32(bg_block_bitmap)       /* Block number of the block bitmap */ \
> -@@ -500,6 +573,7 @@
> - 
> - #define decl8(x) int8 x;
> - #define udecl8(x) uint8 x;
> -+#define utdecl8(x,n) uint8 x[n];
> - #define decl16(x) int16 x;
> - #define udecl16(x) uint16 x;
> - #define decl32(x) int32 x;
> -@@ -509,7 +583,7 @@
> - typedef struct
> - {
> - 	superblock_decl
> --	uint32 s_reserved[235];       // Reserved
> -+	uint32 s_reserved[205];       // Reserved
> - } superblock;
> - 
> - typedef struct
> -@@ -527,10 +601,9 @@
> - typedef struct
> - {
> - 	directory_decl
> --	char d_name[0];
> - } directory;
> - 
> --typedef uint8 block[BLOCKSIZE];
> -+typedef uint8 *block;
> - 
> - /* blockwalker fields:
> -    The blockwalker is used to access all the blocks of a file (including
> -@@ -567,23 +640,41 @@
> - 	uint32 bptind;
> - } blockwalker;
> - 
> -+#define HDLINK_CNT   16
> -+struct hdlink_s
> -+{
> -+	uint32	src_inode;
> -+	uint32	dst_nod;
> -+};
> -+
> -+struct hdlinks_s
> -+{
> -+	int32 count;
> -+	struct hdlink_s *hdl;
> -+};
> - 
> - /* Filesystem structure that support groups */
> --#if BLOCKSIZE == 1024
> - typedef struct
> - {
> --	block zero;            // The famous block 0
> --	superblock sb;         // The superblock
> --	groupdescriptor gd[0]; // The group descriptors
> -+	FILE *f;
> -+	superblock *sb;
> -+	int swapit;
> -+	int32 hdlink_cnt;
> -+	struct hdlinks_s hdlinks;
> -+
> -+	int holes;
> -+
> -+	listcache blks;
> -+	listcache gds;
> -+	listcache inodes;
> -+	listcache blkmaps;
> - } filesystem;
> --#else
> --#error UNHANDLED BLOCKSIZE
> --#endif
> - 
> - // now the endianness swap
> - 
> - #undef decl8
> - #undef udecl8
> -+#undef utdecl8
> - #undef decl16
> - #undef udecl16
> - #undef decl32
> -@@ -592,28 +683,13 @@
> - 
> - #define decl8(x)
> - #define udecl8(x)
> -+#define utdecl8(x,n)
> - #define decl16(x) this->x = swab16(this->x);
> - #define udecl16(x) this->x = swab16(this->x);
> - #define decl32(x) this->x = swab32(this->x);
> - #define udecl32(x) this->x = swab32(this->x);
> - #define utdecl32(x,n) { int i; for(i=0; i<n; i++) this->x[i] = swab32(this->x[i]); }
> - 
> --#define HDLINK_CNT   16
> --static int32 hdlink_cnt = HDLINK_CNT;
> --struct hdlink_s
> --{
> --	uint32	src_inode;
> --	uint32	dst_nod;
> --};
> --
> --struct hdlinks_s 
> --{
> --	int32 count;
> --	struct hdlink_s *hdl;
> --};
> --
> --static struct hdlinks_s hdlinks;
> --
> - static void
> - swap_sb(superblock *sb)
> - {
> -@@ -633,9 +709,24 @@
> - static void
> - swap_nod(inode *nod)
> - {
> -+	uint32 nblk;
> -+
> - #define this nod
> - 	inode_decl
> - #undef this
> -+
> -+	// block and character inodes store the major and minor in the
> -+	// i_block, so we need to unswap to get those.  Also, if it's
> -+	// zero iblocks, put the data back like it belongs.
> -+	nblk = nod->i_blocks / INOBLK;
> -+	if ((nod->i_size && !nblk)
> -+	    || ((nod->i_mode & FM_IFBLK) == FM_IFBLK)
> -+	    || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
> -+	{
> -+		int i;
> -+		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
> -+			nod->i_block[i] = swab32(nod->i_block[i]);
> -+	}
> - }
> - 
> - static void
> -@@ -657,6 +748,7 @@
> - 
> - #undef decl8
> - #undef udecl8
> -+#undef utdecl8
> - #undef decl16
> - #undef udecl16
> - #undef decl32
> -@@ -770,15 +862,15 @@
> - }
> - 
> - int
> --is_hardlink(ino_t inode)
> -+is_hardlink(filesystem *fs, ino_t inode)
> - {
> - 	int i;
> - 
> --	for(i = 0; i < hdlinks.count; i++) {
> --		if(hdlinks.hdl[i].src_inode == inode)
> -+	for(i = 0; i < fs->hdlinks.count; i++) {
> -+		if(fs->hdlinks.hdl[i].src_inode == inode)
> - 			return i;
> - 	}
> --	return -1;		
> -+	return -1;
> - }
> - 
> - // printf helper macro
> -@@ -789,6 +881,8 @@
> - get_workblk(void)
> - {
> - 	unsigned char* b=calloc(1,BLOCKSIZE);
> -+	if (!b)
> -+		error_msg_and_die("get_workblk() failed, out of memory");
> - 	return b;
> - }
> - static inline void
> -@@ -811,24 +905,464 @@
> - 	return b[(item-1) / 8] & (1 << ((item-1) % 8));
> - }
> - 
> --// return a given block from a filesystem
> -+// Used by get_blk/put_blk to hold information about a block owned
> -+// by the user.
> -+typedef struct
> -+{
> -+	cache_link link;
> -+
> -+	filesystem *fs;
> -+	uint32 blk;
> -+	uint8 *b;
> -+	uint32 usecount;
> -+} blk_info;
> -+
> -+#define MAX_FREE_CACHE_BLOCKS 100
> -+
> -+static uint32
> -+blk_elem_val(cache_link *elem)
> -+{
> -+	blk_info *bi = container_of(elem, blk_info, link);
> -+	return bi->blk;
> -+}
> -+
> -+static void
> -+blk_freed(cache_link *elem)
> -+{
> -+	blk_info *bi = container_of(elem, blk_info, link);
> -+
> -+	if (fseeko(bi->fs->f, ((off_t) bi->blk) * BLOCKSIZE, SEEK_SET))
> -+		perror_msg_and_die("fseek");
> -+	if (fwrite(bi->b, BLOCKSIZE, 1, bi->fs->f) != 1)
> -+		perror_msg_and_die("get_blk: write");
> -+	free(bi->b);
> -+	free(bi);
> -+}
> -+
> -+// Return a given block from a filesystem.  Make sure to call
> -+// put_blk when you are done with it.
> - static inline uint8 *
> --get_blk(filesystem *fs, uint32 blk)
> -+get_blk(filesystem *fs, uint32 blk, blk_info **rbi)
> - {
> --	return (uint8*)fs + blk*BLOCKSIZE;
> -+	cache_link *curr;
> -+	blk_info *bi;
> -+
> -+	if (blk >= fs->sb->s_blocks_count)
> -+		error_msg_and_die("Internal error, block out of range");
> -+
> -+	curr = cache_find(&fs->blks, blk);
> -+	if (curr) {
> -+		bi = container_of(curr, blk_info, link);
> -+		bi->usecount++;
> -+		goto out;
> -+	}
> -+
> -+	bi = malloc(sizeof(*bi));
> -+	if (!bi)
> -+		error_msg_and_die("get_blk: out of memory");
> -+	bi->fs = fs;
> -+	bi->blk = blk;
> -+	bi->usecount = 1;
> -+	bi->b = malloc(BLOCKSIZE);
> -+	if (!bi->b)
> -+		error_msg_and_die("get_blk: out of memory");
> -+	cache_add(&fs->blks, &bi->link);
> -+	if (fseeko(fs->f, ((off_t) blk) * BLOCKSIZE, SEEK_SET))
> -+		perror_msg_and_die("fseek");
> -+	if (fread(bi->b, BLOCKSIZE, 1, fs->f) != 1) {
> -+		if (ferror(fs->f))
> -+			perror_msg_and_die("fread");
> -+		memset(bi->b, 0, BLOCKSIZE);
> -+	}
> -+
> -+out:
> -+	*rbi = bi;
> -+	return bi->b;
> - }
> - 
> - // return a given inode from a filesystem
> --static inline inode *
> --get_nod(filesystem *fs, uint32 nod)
> -+static inline void
> -+put_blk(blk_info *bi)
> -+{
> -+	if (bi->usecount == 0)
> -+		error_msg_and_die("Internal error: put_blk usecount zero");
> -+	bi->usecount--;
> -+	if (bi->usecount == 0)
> -+		/* Free happens in the cache code */
> -+		cache_item_set_unused(&bi->fs->blks, &bi->link);
> -+}
> -+
> -+typedef struct
> - {
> --	int grp,offset;
> -+	cache_link link;
> -+
> -+	filesystem *fs;
> -+	int gds;
> -+	blk_info *bi;
> -+	groupdescriptor *gd;
> -+	uint32 usecount;
> -+} gd_info;
> -+
> -+#define MAX_FREE_CACHE_GDS 100
> -+
> -+static uint32
> -+gd_elem_val(cache_link *elem)
> -+{
> -+	gd_info *gi = container_of(elem, gd_info, link);
> -+	return gi->gds;
> -+}
> -+
> -+static void
> -+gd_freed(cache_link *elem)
> -+{
> -+	gd_info *gi = container_of(elem, gd_info, link);
> -+
> -+	if (gi->fs->swapit)
> -+		swap_gd(gi->gd);
> -+	put_blk(gi->bi);
> -+	free(gi);
> -+}
> -+
> -+#define GDS_START ((SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE + BLOCKSIZE - 1) / BLOCKSIZE)
> -+#define GDS_PER_BLOCK (BLOCKSIZE / sizeof(groupdescriptor))
> -+// the group descriptors are aligned on the block size
> -+static inline groupdescriptor *
> -+get_gd(filesystem *fs, uint32 no, gd_info **rgi)
> -+{
> -+	uint32 gdblk;
> -+	uint32 offset;
> -+	gd_info *gi;
> -+	cache_link *curr;
> -+
> -+	curr = cache_find(&fs->gds, no);
> -+	if (curr) {
> -+		gi = container_of(curr, gd_info, link);
> -+		gi->usecount++;
> -+		goto out;
> -+	}
> -+
> -+	gi = malloc(sizeof(*gi));
> -+	if (!gi)
> -+		error_msg_and_die("get_gd: out of memory");
> -+	gi->fs = fs;
> -+	gi->gds = no;
> -+	gi->usecount = 1;
> -+	gdblk = GDS_START + (no / GDS_PER_BLOCK);
> -+	offset = no % GDS_PER_BLOCK;
> -+	gi->gd = ((groupdescriptor *) get_blk(fs, gdblk, &gi->bi)) + offset;
> -+	cache_add(&fs->gds, &gi->link);
> -+	if (fs->swapit)
> -+		swap_gd(gi->gd);
> -+ out:
> -+	*rgi = gi;
> -+
> -+	return gi->gd;
> -+}
> -+
> -+static inline void
> -+put_gd(gd_info *gi)
> -+{
> -+	if (gi->usecount == 0)
> -+		error_msg_and_die("Internal error: put_gd usecount zero");
> -+
> -+	gi->usecount--;
> -+	if (gi->usecount == 0)
> -+		/* Free happens in the cache code */
> -+		cache_item_set_unused(&gi->fs->gds, &gi->link);
> -+}
> -+
> -+// Used by get_blkmap/put_blkmap to hold information about an block map
> -+// owned by the user.
> -+typedef struct
> -+{
> -+	cache_link link;
> -+
> -+	filesystem *fs;
> -+	uint32 blk;
> -+	uint8 *b;
> -+	blk_info *bi;
> -+	uint32 usecount;
> -+} blkmap_info;
> -+
> -+#define MAX_FREE_CACHE_BLOCKMAPS 100
> -+
> -+static uint32
> -+blkmap_elem_val(cache_link *elem)
> -+{
> -+	blkmap_info *bmi = container_of(elem, blkmap_info, link);
> -+	return bmi->blk;
> -+}
> -+
> -+static void
> -+blkmap_freed(cache_link *elem)
> -+{
> -+	blkmap_info *bmi = container_of(elem, blkmap_info, link);
> -+
> -+	if (bmi->fs->swapit)
> -+		swap_block(bmi->b);
> -+	put_blk(bmi->bi);
> -+	free(bmi);
> -+}
> -+
> -+// Return a given block map from a filesystem.  Make sure to call
> -+// put_blkmap when you are done with it.
> -+static inline uint32 *
> -+get_blkmap(filesystem *fs, uint32 blk, blkmap_info **rbmi)
> -+{
> -+	blkmap_info *bmi;
> -+	cache_link *curr;
> -+
> -+	curr = cache_find(&fs->blkmaps, blk);
> -+	if (curr) {
> -+		bmi = container_of(curr, blkmap_info, link);
> -+		bmi->usecount++;
> -+		goto out;
> -+	}
> -+
> -+	bmi = malloc(sizeof(*bmi));
> -+	if (!bmi)
> -+		error_msg_and_die("get_blkmap: out of memory");
> -+	bmi->fs = fs;
> -+	bmi->blk = blk;
> -+	bmi->b = get_blk(fs, blk, &bmi->bi);
> -+	bmi->usecount = 1;
> -+	cache_add(&fs->blkmaps, &bmi->link);
> -+
> -+	if (fs->swapit)
> -+		swap_block(bmi->b);
> -+ out:
> -+	*rbmi = bmi;
> -+	return (uint32 *) bmi->b;
> -+}
> -+
> -+static inline void
> -+put_blkmap(blkmap_info *bmi)
> -+{
> -+	if (bmi->usecount == 0)
> -+		error_msg_and_die("Internal error: put_blkmap usecount zero");
> -+
> -+	bmi->usecount--;
> -+	if (bmi->usecount == 0)
> -+		/* Free happens in the cache code */
> -+		cache_item_set_unused(&bmi->fs->blkmaps, &bmi->link);
> -+}
> -+
> -+// Used by get_nod/put_nod to hold information about an inode owned
> -+// by the user.
> -+typedef struct
> -+{
> -+	cache_link link;
> -+
> -+	filesystem *fs;
> -+	uint32 nod;
> -+	uint8 *b;
> -+	blk_info *bi;
> - 	inode *itab;
> -+	uint32 usecount;
> -+} nod_info;
> -+
> -+#define MAX_FREE_CACHE_INODES 100
> -+
> -+static uint32
> -+inode_elem_val(cache_link *elem)
> -+{
> -+	nod_info *ni = container_of(elem, nod_info, link);
> -+	return ni->nod;
> -+}
> -+
> -+static void
> -+inode_freed(cache_link *elem)
> -+{
> -+	nod_info *ni = container_of(elem, nod_info, link);
> -+
> -+	if (ni->fs->swapit)
> -+		swap_nod(ni->itab);
> -+	put_blk(ni->bi);
> -+	free(ni);
> -+}
> -+
> -+#define INODES_PER_BLOCK (BLOCKSIZE / sizeof(inode))
> - 
> --	offset = GRP_IBM_OFFSET(fs,nod);
> -+// return a given inode from a filesystem
> -+static inline inode *
> -+get_nod(filesystem *fs, uint32 nod, nod_info **rni)
> -+{
> -+	uint32 grp, boffset, offset;
> -+	cache_link *curr;
> -+	groupdescriptor *gd;
> -+	gd_info *gi;
> -+	nod_info *ni;
> -+
> -+	curr = cache_find(&fs->inodes, nod);
> -+	if (curr) {
> -+		ni = container_of(curr, nod_info, link);
> -+		ni->usecount++;
> -+		goto out;
> -+	}
> -+
> -+	ni = malloc(sizeof(*ni));
> -+	if (!ni)
> -+		error_msg_and_die("get_nod: out of memory");
> -+	ni->fs = fs;
> -+	ni->nod = nod;
> -+	ni->usecount = 1;
> -+	cache_add(&fs->inodes, &ni->link);
> -+
> -+	offset = GRP_IBM_OFFSET(fs,nod) - 1;
> -+	boffset = offset / INODES_PER_BLOCK;
> -+	offset %= INODES_PER_BLOCK;
> - 	grp = GRP_GROUP_OF_INODE(fs,nod);
> --	itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table);
> --	return itab+offset-1;
> -+	gd = get_gd(fs, grp, &gi);
> -+	ni->b = get_blk(fs, gd->bg_inode_table + boffset, &ni->bi);
> -+	ni->itab = ((inode *) ni->b) + offset;
> -+	if (fs->swapit)
> -+		swap_nod(ni->itab);
> -+	put_gd(gi);
> -+ out:
> -+	*rni = ni;
> -+	return ni->itab;
> -+}
> -+
> -+static inline void
> -+put_nod(nod_info *ni)
> -+{
> -+	if (ni->usecount == 0)
> -+		error_msg_and_die("Internal error: put_nod usecount zero");
> -+
> -+	ni->usecount--;
> -+	if (ni->usecount == 0)
> -+		/* Free happens in the cache code */
> -+		cache_item_set_unused(&ni->fs->inodes, &ni->link);
> -+}
> -+
> -+// Used to hold state information while walking a directory inode.
> -+typedef struct
> -+{
> -+	directory d;
> -+	filesystem *fs;
> -+	uint32 nod;
> -+	directory *last_d;
> -+	uint8 *b;
> -+	blk_info *bi;
> -+} dirwalker;
> -+
> -+// Start a directory walk on the given inode.  You must pass in a
> -+// dirwalker structure, then use that dirwalker for future operations.
> -+// Call put_dir when you are done walking the directory.
> -+static inline directory *
> -+get_dir(filesystem *fs, uint32 nod, dirwalker *dw)
> -+{
> -+	dw->fs = fs;
> -+	dw->b = get_blk(fs, nod, &dw->bi);
> -+	dw->nod = nod;
> -+	dw->last_d = (directory *) dw->b;
> -+
> -+	memcpy(&dw->d, dw->last_d, sizeof(directory));
> -+	if (fs->swapit)
> -+		swap_dir(&dw->d);
> -+	return &dw->d;
> -+}
> -+
> -+// Move to the next directory.
> -+static inline directory *
> -+next_dir(dirwalker *dw)
> -+{
> -+	directory *next_d = (directory *)((int8*)dw->last_d + dw->d.d_rec_len);
> -+
> -+	if (dw->fs->swapit)
> -+		swap_dir(&dw->d);
> -+	memcpy(dw->last_d, &dw->d, sizeof(directory));
> -+
> -+	if (((int8 *) next_d) >= ((int8 *) dw->b + BLOCKSIZE))
> -+		return NULL;
> -+
> -+	dw->last_d = next_d;
> -+	memcpy(&dw->d, next_d, sizeof(directory));
> -+	if (dw->fs->swapit)
> -+		swap_dir(&dw->d);
> -+	return &dw->d;
> -+}
> -+
> -+// Call then when you are done with the directory walk.
> -+static inline void
> -+put_dir(dirwalker *dw)
> -+{
> -+	if (dw->fs->swapit)
> -+		swap_dir(&dw->d);
> -+	memcpy(dw->last_d, &dw->d, sizeof(directory));
> -+
> -+	if (dw->nod == 0)
> -+		free_workblk(dw->b);
> -+	else
> -+		put_blk(dw->bi);
> -+}
> -+
> -+// Create a new directory block with the given inode as it's destination
> -+// and append it to the current dirwalker.
> -+static directory *
> -+new_dir(filesystem *fs, uint32 dnod, const char *name, int nlen, dirwalker *dw)
> -+{
> -+	directory *d;
> -+
> -+	dw->fs = fs;
> -+	dw->b = get_workblk();
> -+	dw->nod = 0;
> -+	dw->last_d = (directory *) dw->b;
> -+	d = &dw->d;
> -+	d->d_inode = dnod;
> -+	d->d_rec_len = BLOCKSIZE;
> -+	d->d_name_len = nlen;
> -+	strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
> -+	return d;
> -+}
> -+
> -+// Shrink the current directory entry, make a new one with the free
> -+// space, and return the new directory entry (making it current).
> -+static inline directory *
> -+shrink_dir(dirwalker *dw, uint32 nod, const char *name, int nlen)
> -+{
> -+	int reclen, preclen;
> -+	directory *d = &dw->d;
> -+
> -+	reclen = d->d_rec_len;
> -+	d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
> -+	preclen = d->d_rec_len;
> -+	reclen -= preclen;
> -+	if (dw->fs->swapit)
> -+		swap_dir(&dw->d);
> -+	memcpy(dw->last_d, &dw->d, sizeof(directory));
> -+
> -+	dw->last_d = (directory *) (((int8 *) dw->last_d) + preclen);
> -+	d->d_rec_len = reclen;
> -+	d->d_inode = nod;
> -+	d->d_name_len = nlen;
> -+	strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
> -+
> -+	return d;
> -+}
> -+
> -+// Return the current block the directory is walking
> -+static inline uint8 *
> -+dir_data(dirwalker *dw)
> -+{
> -+	return dw->b;
> -+}
> -+
> -+// Return the pointer to the name for the current directory
> -+static inline char *
> -+dir_name(dirwalker *dw)
> -+{
> -+	return ((char *) dw->last_d) + sizeof(directory);
> -+}
> -+
> -+// Set the name for the current directory.  Note that this doesn't
> -+// verify that there is space for the directory name, you must do
> -+// that yourself.
> -+static void
> -+dir_set_name(dirwalker *dw, const char *name, int nlen)
> -+{
> -+	dw->d.d_name_len = nlen;
> -+	strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
> - }
> - 
> - // allocate a given block/inode in the bitmap
> -@@ -870,21 +1404,34 @@
> - {
> - 	uint32 bk=0;
> - 	uint32 grp,nbgroups;
> -+	blk_info *bi;
> -+	groupdescriptor *gd;
> -+	gd_info *gi;
> - 
> - 	grp = GRP_GROUP_OF_INODE(fs,nod);
> - 	nbgroups = GRP_NBGROUPS(fs);
> --	if(!(bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0))) {
> --		for(grp=0;grp<nbgroups && !bk;grp++)
> --			bk=allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap),0);
> -+	gd = get_gd(fs, grp, &gi);
> -+	bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0);
> -+	GRP_PUT_GROUP_BBM(bi);
> -+	put_gd(gi);
> -+	if (!bk) {
> -+		for (grp=0; grp<nbgroups && !bk; grp++) {
> -+			gd = get_gd(fs, grp, &gi);
> -+			bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0);
> -+			GRP_PUT_GROUP_BBM(bi);
> -+			put_gd(gi);
> -+		}
> - 		grp--;
> - 	}
> - 	if (!bk)
> - 		error_msg_and_die("couldn't allocate a block (no free space)");
> --	if(!(fs->gd[grp].bg_free_blocks_count--))
> -+	gd = get_gd(fs, grp, &gi);
> -+	if(!(gd->bg_free_blocks_count--))
> - 		error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)",grp);
> --	if(!(fs->sb.s_free_blocks_count--))
> -+	put_gd(gi);
> -+	if(!(fs->sb->s_free_blocks_count--))
> - 		error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
> --	return fs->sb.s_blocks_per_group*grp + bk;
> -+	return fs->sb->s_first_data_block + fs->sb->s_blocks_per_group*grp + (bk-1);
> - }
> - 
> - // free a block
> -@@ -892,12 +1439,18 @@
> - free_blk(filesystem *fs, uint32 bk)
> - {
> - 	uint32 grp;
> --
> --	grp = bk / fs->sb.s_blocks_per_group;
> --	bk %= fs->sb.s_blocks_per_group;
> --	deallocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), bk);
> --	fs->gd[grp].bg_free_blocks_count++;
> --	fs->sb.s_free_blocks_count++;
> -+	blk_info *bi;
> -+	gd_info *gi;
> -+	groupdescriptor *gd;
> -+
> -+	grp = bk / fs->sb->s_blocks_per_group;
> -+	bk %= fs->sb->s_blocks_per_group;
> -+	gd = get_gd(fs, grp, &gi);
> -+	deallocate(GRP_GET_GROUP_BBM(fs, gd, &bi), bk);
> -+	GRP_PUT_GROUP_BBM(bi);
> -+	gd->bg_free_blocks_count++;
> -+	put_gd(gi);
> -+	fs->sb->s_free_blocks_count++;
> - }
> - 
> - // allocate an inode
> -@@ -906,6 +1459,9 @@
> - {
> - 	uint32 nod,best_group=0;
> - 	uint32 grp,nbgroups,avefreei;
> -+	blk_info *bi;
> -+	gd_info *gi, *bestgi;
> -+	groupdescriptor *gd, *bestgd;
> - 
> - 	nbgroups = GRP_NBGROUPS(fs);
> - 
> -@@ -914,22 +1470,32 @@
> - 	/* find the one with the most free blocks and allocate node there     */
> - 	/* Idea from find_group_dir in fs/ext2/ialloc.c in 2.4.19 kernel      */
> - 	/* We do it for all inodes.                                           */
> --	avefreei  =  fs->sb.s_free_inodes_count / nbgroups;
> -+	avefreei  =  fs->sb->s_free_inodes_count / nbgroups;
> -+	bestgd = get_gd(fs, best_group, &bestgi);
> - 	for(grp=0; grp<nbgroups; grp++) {
> --		if (fs->gd[grp].bg_free_inodes_count < avefreei ||
> --		    fs->gd[grp].bg_free_inodes_count == 0)
> -+		gd = get_gd(fs, grp, &gi);
> -+		if (gd->bg_free_inodes_count < avefreei ||
> -+		    gd->bg_free_inodes_count == 0) {
> -+			put_gd(gi);
> - 			continue;
> --		if (!best_group || 
> --			fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count)
> -+		}
> -+		if (!best_group || gd->bg_free_blocks_count > bestgd->bg_free_blocks_count) {
> -+			put_gd(bestgi);
> - 			best_group = grp;
> -+			bestgd = gd;
> -+			bestgi = gi;
> -+		} else
> -+			put_gd(gi);
> - 	}
> --	if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0)))
> -+	if (!(nod = allocate(GRP_GET_GROUP_IBM(fs, bestgd, &bi), 0)))
> - 		error_msg_and_die("couldn't allocate an inode (no free inode)");
> --	if(!(fs->gd[best_group].bg_free_inodes_count--))
> -+	GRP_PUT_GROUP_IBM(bi);
> -+	if(!(bestgd->bg_free_inodes_count--))
> - 		error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
> --	if(!(fs->sb.s_free_inodes_count--))
> -+	put_gd(bestgi);
> -+	if(!(fs->sb->s_free_inodes_count--))
> - 		error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
> --	return fs->sb.s_inodes_per_group*best_group+nod;
> -+	return fs->sb->s_inodes_per_group*best_group+nod;
> - }
> - 
> - // print a bitmap allocation
> -@@ -962,30 +1528,40 @@
> - //				  used after being freed, so once you start
> - //				  freeing blocks don't stop until the end of
> - //				  the file. moreover, i_blocks isn't updated.
> --//				  in fact, don't do that, just use extend_blk
> - // if hole!=0, create a hole in the file
> - static uint32
> - walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, int32 *create, uint32 hole)
> - {
> - 	uint32 *bkref = 0;
> -+	uint32 bk = 0;
> -+	blkmap_info *bmi1 = NULL, *bmi2 = NULL, *bmi3 = NULL;
> - 	uint32 *b;
> - 	int extend = 0, reduce = 0;
> -+	inode *inod;
> -+	nod_info *ni;
> -+	uint32 *iblk;
> -+
> - 	if(create && (*create) < 0)
> - 		reduce = 1;
> --	if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK)
> -+	inod = get_nod(fs, nod, &ni);
> -+	if(bw->bnum >= inod->i_blocks / INOBLK)
> - 	{
> - 		if(create && (*create) > 0)
> - 		{
> - 			(*create)--;
> - 			extend = 1;
> - 		}
> --		else	
> -+		else
> -+		{
> -+			put_nod(ni);
> - 			return WALK_END;
> -+		}
> - 	}
> -+	iblk = inod->i_block;
> - 	// first direct block
> - 	if(bw->bpdir == EXT2_INIT_BLOCK)
> - 	{
> --		bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0];
> -+		bkref = &iblk[bw->bpdir = 0];
> - 		if(extend) // allocate first block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> - 		if(reduce) // free first block
> -@@ -994,7 +1570,7 @@
> - 	// direct block
> - 	else if(bw->bpdir < EXT2_NDIR_BLOCKS)
> - 	{
> --		bkref = &get_nod(fs, nod)->i_block[++bw->bpdir];
> -+		bkref = &iblk[++bw->bpdir];
> - 		if(extend) // allocate block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> - 		if(reduce) // free block
> -@@ -1007,10 +1583,10 @@
> - 		bw->bpdir = EXT2_IND_BLOCK;
> - 		bw->bpind = 0;
> - 		if(extend) // allocate indirect block
> --			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
> -+			iblk[bw->bpdir] = alloc_blk(fs,nod);
> - 		if(reduce) // free indirect block
> --			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> -+			free_blk(fs, iblk[bw->bpdir]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> - 		bkref = &b[bw->bpind];
> - 		if(extend) // allocate first block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1021,7 +1597,7 @@
> - 	else if((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
> - 	{
> - 		bw->bpind++;
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> - 		bkref = &b[bw->bpind];
> - 		if(extend) // allocate block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1036,15 +1612,15 @@
> - 		bw->bpind = 0;
> - 		bw->bpdind = 0;
> - 		if(extend) // allocate double indirect block
> --			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
> -+			iblk[bw->bpdir] = alloc_blk(fs,nod);
> - 		if(reduce) // free double indirect block
> --			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> -+			free_blk(fs, iblk[bw->bpdir]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> - 		if(extend) // allocate first indirect block
> - 			b[bw->bpind] = alloc_blk(fs,nod);
> - 		if(reduce) // free  firstindirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> - 		bkref = &b[bw->bpdind];
> - 		if(extend) // allocate first block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1055,8 +1631,8 @@
> - 	else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1))
> - 	{
> - 		bw->bpdind++;
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> - 		bkref = &b[bw->bpdind];
> - 		if(extend) // allocate block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1069,12 +1645,12 @@
> - 		bw->bnum++;
> - 		bw->bpdind = 0;
> - 		bw->bpind++;
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> - 		if(extend) // allocate indirect block
> - 			b[bw->bpind] = alloc_blk(fs,nod);
> - 		if(reduce) // free indirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> - 		bkref = &b[bw->bpdind];
> - 		if(extend) // allocate first block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1094,20 +1670,20 @@
> - 		bw->bpdind = 0;
> - 		bw->bptind = 0;
> - 		if(extend) // allocate triple indirect block
> --			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
> -+			iblk[bw->bpdir] = alloc_blk(fs,nod);
> - 		if(reduce) // free triple indirect block
> --			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> -+			free_blk(fs, iblk[bw->bpdir]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> - 		if(extend) // allocate first double indirect block
> - 			b[bw->bpind] = alloc_blk(fs,nod);
> - 		if(reduce) // free first double indirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> - 		if(extend) // allocate first indirect block
> - 			b[bw->bpdind] = alloc_blk(fs,nod);
> - 		if(reduce) // free first indirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpdind]);
> -+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
> - 		bkref = &b[bw->bptind];
> - 		if(extend) // allocate first data block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1121,9 +1697,9 @@
> - 		  (bw->bptind < BLOCKSIZE/4 -1) )
> - 	{
> - 		bw->bptind++;
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpdind]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> -+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
> - 		bkref = &b[bw->bptind];
> - 		if(extend) // allocate data block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1140,13 +1716,13 @@
> - 		bw->bnum++;
> - 		bw->bptind = 0;
> - 		bw->bpdind++;
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> - 		if(extend) // allocate single indirect block
> - 			b[bw->bpdind] = alloc_blk(fs,nod);
> - 		if(reduce) // free indirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpdind]);
> -+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
> - 		bkref = &b[bw->bptind];
> - 		if(extend) // allocate first data block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1163,17 +1739,17 @@
> - 		bw->bpdind = 0;
> - 		bw->bptind = 0;
> - 		bw->bpind++;
> --		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
> -+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
> - 		if(extend) // allocate double indirect block
> - 			b[bw->bpind] = alloc_blk(fs,nod);
> - 		if(reduce) // free double indirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpind]);
> -+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
> - 		if(extend) // allocate single indirect block
> - 			b[bw->bpdind] = alloc_blk(fs,nod);
> - 		if(reduce) // free indirect block
> - 			free_blk(fs, b[bw->bpind]);
> --		b = (uint32*)get_blk(fs, b[bw->bpdind]);
> -+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
> - 		bkref = &b[bw->bptind];
> - 		if(extend) // allocate first block
> - 			*bkref = hole ? 0 : alloc_blk(fs,nod);
> -@@ -1184,56 +1760,105 @@
> - 		error_msg_and_die("file too big !"); 
> - 	/* End change for walking triple indirection */
> - 
> --	if(*bkref)
> --	{
> -+	bk = *bkref;
> -+	if (bmi3)
> -+		put_blkmap(bmi3);
> -+	if (bmi2)
> -+		put_blkmap(bmi2);
> -+	if (bmi1)
> -+		put_blkmap(bmi1);
> -+
> -+	if(bk)
> -+	{
> -+		blk_info *bi;
> -+		gd_info *gi;
> -+		uint8 *block;
> - 		bw->bnum++;
> --		if(!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref)))
> --			error_msg_and_die("[block %d of inode %d is unallocated !]", *bkref, nod);
> -+		block = GRP_GET_BLOCK_BITMAP(fs,bk,&bi,&gi);
> -+		if(!reduce && !allocated(block, GRP_BBM_OFFSET(fs,bk)))
> -+			error_msg_and_die("[block %d of inode %d is unallocated !]", bk, nod);
> -+		GRP_PUT_BLOCK_BITMAP(bi, gi);
> - 	}
> - 	if(extend)
> --		get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
> --	return *bkref;
> -+		inod->i_blocks = bw->bnum * INOBLK;
> -+	put_nod(ni);
> -+	return bk;
> - }
> - 
> --// add blocks to an inode (file/dir/etc...)
> --static void
> --extend_blk(filesystem *fs, uint32 nod, block b, int amount)
> -+typedef struct
> - {
> --	int create = amount;
> --	blockwalker bw, lbw;
> --	uint32 bk;
> --	init_bw(&bw);
> --	if(amount < 0)
> --	{
> --		uint32 i;
> --		for(i = 0; i < get_nod(fs, nod)->i_blocks / INOBLK + amount; i++)
> --			walk_bw(fs, nod, &bw, 0, 0);
> --		while(walk_bw(fs, nod, &bw, &create, 0) != WALK_END)
> -+	blockwalker bw;
> -+	uint32 nod;
> -+	nod_info *ni;
> -+	inode *inod;
> -+} inode_pos;
> -+#define INODE_POS_TRUNCATE 0
> -+#define INODE_POS_EXTEND 1
> -+
> -+// Call this to set up an ipos structure for future use with
> -+// extend_inode_blk to append blocks to the given inode.  If
> -+// op is INODE_POS_TRUNCATE, the inode is truncated to zero size.
> -+// If op is INODE_POS_EXTEND, the position is moved to the end
> -+// of the inode's data blocks.
> -+// Call inode_pos_finish when done with the inode_pos structure.
> -+static void
> -+inode_pos_init(filesystem *fs, inode_pos *ipos, uint32 nod, int op,
> -+	       blockwalker *endbw)
> -+{
> -+	blockwalker lbw;
> -+
> -+	init_bw(&ipos->bw);
> -+	ipos->nod = nod;
> -+	ipos->inod = get_nod(fs, nod, &ipos->ni);
> -+	if (op == INODE_POS_TRUNCATE) {
> -+		int32 create = -1;
> -+		while(walk_bw(fs, nod, &ipos->bw, &create, 0) != WALK_END)
> - 			/*nop*/;
> --		get_nod(fs, nod)->i_blocks += amount * INOBLK;
> -+		ipos->inod->i_blocks = 0;
> - 	}
> --	else
> -+
> -+	if (endbw)
> -+		ipos->bw = *endbw;
> -+	else {
> -+		/* Seek to the end */
> -+		init_bw(&ipos->bw);
> -+		lbw = ipos->bw;
> -+		while(walk_bw(fs, nod, &ipos->bw, 0, 0) != WALK_END)
> -+			lbw = ipos->bw;
> -+		ipos->bw = lbw;
> -+	}
> -+}
> -+
> -+// Clean up the inode_pos structure.
> -+static void
> -+inode_pos_finish(filesystem *fs, inode_pos *ipos)
> -+{
> -+	put_nod(ipos->ni);
> -+}
> -+
> -+// add blocks to an inode (file/dir/etc...) at the given position.
> -+// This will only work when appending to the end of an inode.
> -+static void
> -+extend_inode_blk(filesystem *fs, inode_pos *ipos, block b, int amount)
> -+{
> -+	uint32 bk;
> -+	uint32 pos;
> -+
> -+	if (amount < 0)
> -+		error_msg_and_die("extend_inode_blk: Got negative amount");
> -+
> -+	for (pos = 0; amount; pos += BLOCKSIZE)
> - 	{
> --		lbw = bw;
> --		while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
> --			lbw = bw;
> --		bw = lbw;
> --		while(create)
> --		{
> --			int i, copyb = 0;
> --			if(!(fs->sb.s_reserved[200] & OP_HOLES))
> --				copyb = 1;
> --			else
> --				for(i = 0; i < BLOCKSIZE / 4; i++)
> --					if(((int32*)(b + BLOCKSIZE * (amount - create)))[i])
> --					{
> --						copyb = 1;
> --						break;
> --					}
> --			if((bk = walk_bw(fs, nod, &bw, &create, !copyb)) == WALK_END)
> --				break;
> --			if(copyb)
> --				memcpy(get_blk(fs, bk), b + BLOCKSIZE * (amount - create - 1), BLOCKSIZE);
> -+		int hole = (fs->holes && is_blk_empty(b + pos));
> -+
> -+		bk = walk_bw(fs, ipos->nod, &ipos->bw, &amount, hole);
> -+		if (bk == WALK_END)
> -+			error_msg_and_die("extend_inode_blk: extend failed");
> -+		if (!hole) {
> -+			blk_info *bi;
> -+			uint8 *block = get_blk(fs, bk, &bi);
> -+			memcpy(block, b + pos, BLOCKSIZE);
> -+			put_blk(bi);
> - 		}
> - 	}
> - }
> -@@ -1242,15 +1867,17 @@
> - static void
> - add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name)
> - {
> --	blockwalker bw;
> -+	blockwalker bw, lbw;
> - 	uint32 bk;
> --	uint8 *b;
> - 	directory *d;
> -+	dirwalker dw;
> - 	int reclen, nlen;
> - 	inode *node;
> - 	inode *pnode;
> -+	nod_info *dni, *ni;
> -+	inode_pos ipos;
> - 
> --	pnode = get_nod(fs, dnod);
> -+	pnode = get_nod(fs, dnod, &dni);
> - 	if((pnode->i_mode & FM_IFMT) != FM_IFDIR)
> - 		error_msg_and_die("can't add '%s' to a non-directory", name);
> - 	if(!*name)
> -@@ -1262,52 +1889,52 @@
> - 	if(reclen > BLOCKSIZE)
> - 		error_msg_and_die("bad name '%s' (too long)", name);
> - 	init_bw(&bw);
> -+	lbw = bw;
> - 	while((bk = walk_bw(fs, dnod, &bw, 0, 0)) != WALK_END) // for all blocks in dir
> - 	{
> --		b = get_blk(fs, bk);
> - 		// for all dir entries in block
> --		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
> -+		for(d = get_dir(fs, bk, &dw); d; d = next_dir(&dw))
> - 		{
> - 			// if empty dir entry, large enough, use it
> - 			if((!d->d_inode) && (d->d_rec_len >= reclen))
> - 			{
> - 				d->d_inode = nod;
> --				node = get_nod(fs, nod);
> -+				node = get_nod(fs, nod, &ni);
> -+				dir_set_name(&dw, name, nlen);
> -+				put_dir(&dw);
> - 				node->i_links_count++;
> --				d->d_name_len = nlen;
> --				strncpy(d->d_name, name, nlen);
> --				return;
> -+				put_nod(ni);
> -+				goto out;
> - 			}
> - 			// if entry with enough room (last one?), shrink it & use it
> - 			if(d->d_rec_len >= (sizeof(directory) + rndup(d->d_name_len, 4) + reclen))
> - 			{
> --				reclen = d->d_rec_len;
> --				d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
> --				reclen -= d->d_rec_len;
> --				d = (directory*) (((int8*)d) + d->d_rec_len);
> --				d->d_rec_len = reclen;
> --				d->d_inode = nod;
> --				node = get_nod(fs, nod);
> -+				d = shrink_dir(&dw, nod, name, nlen);
> -+				put_dir(&dw);
> -+				node = get_nod(fs, nod, &ni);
> - 				node->i_links_count++;
> --				d->d_name_len = nlen;
> --				strncpy(d->d_name, name, nlen);
> --				return;
> -+				put_nod(ni);
> -+				goto out;
> - 			}
> - 		}
> -+		put_dir(&dw);
> -+		lbw = bw;
> - 	}
> - 	// we found no free entry in the directory, so we add a block
> --	if(!(b = get_workblk()))
> --		error_msg_and_die("get_workblk() failed.");
> --	d = (directory*)b;
> --	d->d_inode = nod;
> --	node = get_nod(fs, nod);
> -+	node = get_nod(fs, nod, &ni);
> -+	d = new_dir(fs, nod, name, nlen, &dw);
> - 	node->i_links_count++;
> --	d->d_rec_len = BLOCKSIZE;
> --	d->d_name_len = nlen;
> --	strncpy(d->d_name, name, nlen);
> --	extend_blk(fs, dnod, b, 1);
> --	get_nod(fs, dnod)->i_size += BLOCKSIZE;
> --	free_workblk(b);
> -+	put_nod(ni);
> -+	next_dir(&dw); // Force the data into the buffer
> -+
> -+	inode_pos_init(fs, &ipos, dnod, INODE_POS_EXTEND, &lbw);
> -+	extend_inode_blk(fs, &ipos, dir_data(&dw), 1);
> -+	inode_pos_finish(fs, &ipos);
> -+
> -+	put_dir(&dw);
> -+	pnode->i_size += BLOCKSIZE;
> -+out:
> -+	put_nod(dni);
> - }
> - 
> - // find an entry in a directory
> -@@ -1321,11 +1948,13 @@
> - 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
> - 	{
> - 		directory *d;
> --		uint8 *b;
> --		b = get_blk(fs, bk);
> --		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
> --			if(d->d_inode && (nlen == d->d_name_len) && !strncmp(d->d_name, name, nlen))
> -+		dirwalker dw;
> -+		for (d = get_dir(fs, bk, &dw); d; d=next_dir(&dw))
> -+			if(d->d_inode && (nlen == d->d_name_len) && !strncmp(dir_name(&dw), name, nlen)) {
> -+				put_dir(&dw);
> - 				return d->d_inode;
> -+			}
> -+		put_dir(&dw);
> - 	}
> - 	return 0;
> - }
> -@@ -1356,47 +1985,55 @@
> - 	return nod;
> - }
> - 
> -+// chmod an inode
> -+void
> -+chmod_fs(filesystem *fs, uint32 nod, uint16 mode, uint16 uid, uint16 gid)
> -+{
> -+	inode *node;
> -+	nod_info *ni;
> -+	node = get_nod(fs, nod, &ni);
> -+	node->i_mode = (node->i_mode & ~FM_IMASK) | (mode & FM_IMASK);
> -+	node->i_uid = uid;
> -+	node->i_gid = gid;
> -+	put_nod(ni);
> -+}
> -+
> - // create a simple inode
> - static uint32
> - mknod_fs(filesystem *fs, uint32 parent_nod, const char *name, uint16 mode, uint16 uid, uint16 gid, uint8 major, uint8 minor, uint32 ctime, uint32 mtime)
> - {
> - 	uint32 nod;
> - 	inode *node;
> --	if((nod = find_dir(fs, parent_nod, name)))
> --	{
> --		node = get_nod(fs, nod);
> --		if((node->i_mode & FM_IFMT) != (mode & FM_IFMT))
> --			error_msg_and_die("node '%s' already exists and isn't of the same type", name);
> --		node->i_mode = mode;
> --	}
> --	else
> -+	nod_info *ni;
> -+	gd_info *gi;
> -+
> -+	nod = alloc_nod(fs);
> -+	node = get_nod(fs, nod, &ni);
> -+	node->i_mode = mode;
> -+	add2dir(fs, parent_nod, nod, name);
> -+	switch(mode & FM_IFMT)
> - 	{
> --		nod = alloc_nod(fs);
> --		node = get_nod(fs, nod);
> --		node->i_mode = mode;
> --		add2dir(fs, parent_nod, nod, name);
> --		switch(mode & FM_IFMT)
> --		{
> --			case FM_IFLNK:
> --				mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
> --				break;
> --			case FM_IFBLK:
> --			case FM_IFCHR:
> --				((uint8*)get_nod(fs, nod)->i_block)[0] = minor;
> --				((uint8*)get_nod(fs, nod)->i_block)[1] = major;
> --				break;
> --			case FM_IFDIR:
> --				add2dir(fs, nod, nod, ".");
> --				add2dir(fs, nod, parent_nod, "..");
> --				fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++;
> --				break;
> --		}
> -+	case FM_IFLNK:
> -+		mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
> -+		break;
> -+	case FM_IFBLK:
> -+	case FM_IFCHR:
> -+		((uint8*)node->i_block)[0] = minor;
> -+		((uint8*)node->i_block)[1] = major;
> -+		break;
> -+	case FM_IFDIR:
> -+		add2dir(fs, nod, nod, ".");
> -+		add2dir(fs, nod, parent_nod, "..");
> -+		get_gd(fs,GRP_GROUP_OF_INODE(fs,nod),&gi)->bg_used_dirs_count++;
> -+		put_gd(gi);
> -+		break;
> - 	}
> - 	node->i_uid = uid;
> - 	node->i_gid = gid;
> - 	node->i_atime = mtime;
> - 	node->i_ctime = ctime;
> - 	node->i_mtime = mtime;
> -+	put_nod(ni);
> - 	return nod;
> - }
> - 
> -@@ -1413,33 +2050,73 @@
> - mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 *b, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
> - {
> - 	uint32 nod = mknod_fs(fs, parent_nod, name, FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO, uid, gid, 0, 0, ctime, mtime);
> --	extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
> --	get_nod(fs, nod)->i_size = size;
> --	if(size <= 4 * (EXT2_TIND_BLOCK+1))
> --	{
> --		strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size);
> -+	nod_info *ni;
> -+	inode *node = get_nod(fs, nod, &ni);
> -+	inode_pos ipos;
> -+
> -+	inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
> -+	node->i_size = size;
> -+	if(size < 4 * (EXT2_TIND_BLOCK+1))
> -+	{
> -+		strncpy((char*)node->i_block, (char*)b, size);
> -+		((char*)node->i_block)[size+1] = '\0';
> -+		inode_pos_finish(fs, &ipos);
> -+		put_nod(ni);
> - 		return nod;
> - 	}
> --	extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
> -+	extend_inode_blk(fs, &ipos, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
> -+	inode_pos_finish(fs, &ipos);
> -+	put_nod(ni);
> - 	return nod;
> - }
> - 
> -+static void
> -+fs_upgrade_rev1_largefile(filesystem *fs)
> -+{
> -+	fs->sb->s_rev_level = 1;
> -+	fs->sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
> -+	fs->sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
> -+}
> -+
> -+#define COPY_BLOCKS 16
> -+#define CB_SIZE (COPY_BLOCKS * BLOCKSIZE)
> -+
> - // make a file from a FILE*
> - static uint32
> --mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
> -+mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
> - {
> - 	uint8 * b;
> - 	uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime);
> --	extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
> --	get_nod(fs, nod)->i_size = size;
> --	if (size) {
> --		if(!(b = (uint8*)calloc(rndup(size, BLOCKSIZE), 1)))
> --			error_msg_and_die("not enough mem to read file '%s'", name);
> --		if(f)
> --			fread(b, size, 1, f); // FIXME: ugly. use mmap() ...
> --		extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
> --		free(b);
> --	}
> -+	nod_info *ni;
> -+	inode *node = get_nod(fs, nod, &ni);
> -+	off_t size = 0;
> -+	size_t readbytes;
> -+	inode_pos ipos;
> -+	int fullsize;
> -+
> -+	b = malloc(CB_SIZE);
> -+	if (!b)
> -+		error_msg_and_die("mkfile_fs: out of memory");
> -+	inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
> -+	readbytes = fread(b, 1, CB_SIZE, f);
> -+	while (readbytes) {
> -+		fullsize = rndup(readbytes, BLOCKSIZE);
> -+		// Fill to end of block with zeros.
> -+		memset(b + readbytes, 0, fullsize - readbytes);
> -+		extend_inode_blk(fs, &ipos, b, fullsize / BLOCKSIZE);
> -+		size += readbytes;
> -+		readbytes = fread(b, 1, CB_SIZE, f);
> -+	}
> -+	if (size > 0x7fffffff) {
> -+		if (fs->sb->s_rev_level < 1)
> -+			fs_upgrade_rev1_largefile(fs);
> -+		fs->sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
> -+	}
> -+	node->i_dir_acl = size >> 32;
> -+	node->i_size = size;
> -+	inode_pos_finish(fs, &ipos);
> -+	put_nod(ni);
> -+	free(b);
> - 	return nod;
> - }
> - 
> -@@ -1591,13 +2268,24 @@
> - 				dname = malloc(len + 1);
> - 				for(i = start; i < count; i++)
> - 				{
> -+					uint32 oldnod;
> - 					SNPRINTF(dname, len, "%s%lu", name, i);
> --					mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
> -+					oldnod = find_dir(fs, nod, dname);
> -+					if(oldnod)
> -+						chmod_fs(fs, oldnod, mode, uid, gid);
> -+					else
> -+						mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
> - 				}
> - 				free(dname);
> - 			}
> - 			else
> --				mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
> -+			{
> -+				uint32 oldnod = find_dir(fs, nod, name);
> -+				if(oldnod)
> -+					chmod_fs(fs, oldnod, mode, uid, gid);
> -+				else
> -+					mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
> -+			}
> - 		}
> - 	}
> - 	if (line)
> -@@ -1643,6 +2331,10 @@
> - 			switch(st.st_mode & S_IFMT)
> - 			{
> - 				case S_IFLNK:
> -+					if((st.st_mode & S_IFMT) == S_IFREG || st.st_size >= 4 * (EXT2_TIND_BLOCK+1))
> -+						stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
> -+					stats->ninodes++;
> -+					break;
> - 				case S_IFREG:
> - 					if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1))
> - 						stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
> -@@ -1657,19 +2349,33 @@
> - 					if(chdir(dent->d_name) < 0)
> - 						perror_msg_and_die(dent->d_name);
> - 					add2fs_from_dir(fs, this_nod, squash_uids, squash_perms, fs_timestamp, stats);
> --					chdir("..");
> -+					if (chdir("..") == -1)
> -+						perror_msg_and_die("..");
> -+
> - 					break;
> - 				default:
> - 					break;
> - 			}
> - 		else
> - 		{
> -+			if((nod = find_dir(fs, this_nod, name)))
> -+			{
> -+				error_msg("ignoring duplicate entry %s", name);
> -+				if(S_ISDIR(st.st_mode)) {
> -+					if(chdir(dent->d_name) < 0)
> -+						perror_msg_and_die(name);
> -+					add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
> -+					if (chdir("..") == -1)
> -+						perror_msg_and_die("..");
> -+				}
> -+				continue;
> -+			}
> - 			save_nod = 0;
> - 			/* Check for hardlinks */
> - 			if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) {
> --				int32 hdlink = is_hardlink(st.st_ino);
> -+				int32 hdlink = is_hardlink(fs, st.st_ino);
> - 				if (hdlink >= 0) {
> --					add2dir(fs, this_nod, hdlinks.hdl[hdlink].dst_nod, name);
> -+					add2dir(fs, this_nod, fs->hdlinks.hdl[hdlink].dst_nod, name);
> - 					continue;
> - 				} else {
> - 					save_nod = 1;
> -@@ -1697,8 +2403,12 @@
> - 					free(lnk);
> - 					break;
> - 				case S_IFREG:
> --					fh = xfopen(dent->d_name, "rb");
> --					nod = mkfile_fs(fs, this_nod, name, mode, st.st_size, fh, uid, gid, ctime, mtime);
> -+					fh = fopen(dent->d_name, "rb");
> -+					if (!fh) {
> -+						error_msg("Unable to open file %s", dent->d_name);
> -+						break;
> -+					}
> -+					nod = mkfile_fs(fs, this_nod, name, mode, fh, uid, gid, ctime, mtime);
> - 					fclose(fh);
> - 					break;
> - 				case S_IFDIR:
> -@@ -1706,199 +2416,128 @@
> - 					if(chdir(dent->d_name) < 0)
> - 						perror_msg_and_die(name);
> - 					add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
> --					chdir("..");
> -+					if (chdir("..") == -1)
> -+						perror_msg_and_die("..");
> - 					break;
> - 				default:
> - 					error_msg("ignoring entry %s", name);
> - 			}
> - 			if (save_nod) {
> --				if (hdlinks.count == hdlink_cnt) {
> --					if ((hdlinks.hdl = 
> --						 realloc (hdlinks.hdl, (hdlink_cnt + HDLINK_CNT) *
> -+				if (fs->hdlinks.count == fs->hdlink_cnt) {
> -+					if ((fs->hdlinks.hdl =
> -+						 realloc (fs->hdlinks.hdl, (fs->hdlink_cnt + HDLINK_CNT) *
> - 								  sizeof (struct hdlink_s))) == NULL) {
> - 						error_msg_and_die("Not enough memory");
> - 					}
> --					hdlink_cnt += HDLINK_CNT;
> -+					fs->hdlink_cnt += HDLINK_CNT;
> - 				}
> --				hdlinks.hdl[hdlinks.count].src_inode = st.st_ino;
> --				hdlinks.hdl[hdlinks.count].dst_nod = nod;
> --				hdlinks.count++;
> -+				fs->hdlinks.hdl[fs->hdlinks.count].src_inode = st.st_ino;
> -+				fs->hdlinks.hdl[fs->hdlinks.count].dst_nod = nod;
> -+				fs->hdlinks.count++;
> - 			}
> - 		}
> - 	}
> - 	closedir(dh);
> - }
> - 
> --// endianness swap of x-indirect blocks
> -+// Copy size blocks from src to dst, putting holes in the output
> -+// file (if possible) if the input block is all zeros.
> -+// Copy size blocks from src to dst, putting holes in the output
> -+// file (if possible) if the input block is all zeros.
> - static void
> --swap_goodblocks(filesystem *fs, inode *nod)
> -+copy_file(filesystem *fs, FILE *dst, FILE *src, size_t size)
> - {
> --	uint32 i,j;
> --	int done=0;
> --	uint32 *b,*b2;
> -+	uint8 *b;
> - 
> --	uint32 nblk = nod->i_blocks / INOBLK;
> --	if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
> --		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
> --			nod->i_block[i] = swab32(nod->i_block[i]);
> --	if(nblk <= EXT2_IND_BLOCK)
> --		return;
> --	swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
> --	if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
> --		return;
> --	/* Currently this will fail b'cos the number of blocks as stored
> --	   in i_blocks also includes the indirection blocks (see
> --	   walk_bw). But this function assumes that i_blocks only
> --	   stores the count of data blocks ( Actually according to
> --	   "Understanding the Linux Kernel" (Table 17-3 p502 1st Ed)
> --	   i_blocks IS supposed to store the count of data blocks). so
> --	   with a file of size 268K nblk would be 269.The above check
> --	   will be false even though double indirection hasn't been
> --	   started.This is benign as 0 means block 0 which has been
> --	   zeroed out and therefore points back to itself from any offset
> --	 */
> --	// FIXME: I have fixed that, but I have the feeling the rest of
> --	// ths function needs to be fixed for the same reasons - Xav
> --	assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
> --	for(i = 0; i < BLOCKSIZE/4; i++)
> --		if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
> --			swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
> --	swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
> --	if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
> --		return;
> --	/* Adding support for triple indirection */
> --	b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
> --	for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
> --		b2 = (uint32*)get_blk(fs,b[i]); 
> --		for(j=0; j<BLOCKSIZE/4;j++) {
> --			if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + 
> --				     (BLOCKSIZE/4)*(BLOCKSIZE/4) + 
> --				     i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + 
> --				     j*(BLOCKSIZE/4)) ) 
> --			  swap_block(get_blk(fs,b2[j]));
> --			else {
> --			  done = 1;
> --			  break;
> --			}
> -+	b = malloc(BLOCKSIZE);
> -+	if (!b)
> -+		error_msg_and_die("copy_file: out of memory");
> -+	if (fseek(src, 0, SEEK_SET))
> -+		perror_msg_and_die("fseek");
> -+	if (ftruncate(fileno(dst), 0))
> -+		perror_msg_and_die("copy_file: ftruncate");
> -+	while (size > 0) {
> -+		if (fread(b, BLOCKSIZE, 1, src) != 1)
> -+			perror_msg_and_die("copy failed on read");
> -+		if ((dst != stdout) && fs->holes && is_blk_empty(b)) {
> -+			/* Empty block, just skip it */
> -+			if (fseek(dst, BLOCKSIZE, SEEK_CUR))
> -+				perror_msg_and_die("fseek");
> -+		} else {
> -+			if (fwrite(b, BLOCKSIZE, 1, dst) != 1)
> -+				perror_msg_and_die("copy failed on write");
> - 		}
> --		swap_block((uint8 *)b2);
> -+		size--;
> - 	}
> --	swap_block((uint8 *)b);
> --	return;
> -+	free(b);
> - }
> - 
> --static void
> --swap_badblocks(filesystem *fs, inode *nod)
> -+// Allocate a new filesystem structure, allocate internal memory,
> -+// and initialize the contents.
> -+static filesystem *
> -+alloc_fs(int swapit, char *fname, uint32 nbblocks, FILE *srcfile)
> - {
> --	uint32 i,j;
> --	int done=0;
> --	uint32 *b,*b2;
> -+	filesystem *fs;
> -+	struct stat srcstat, dststat;
> - 
> --	uint32 nblk = nod->i_blocks / INOBLK;
> --	if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
> --		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
> --			nod->i_block[i] = swab32(nod->i_block[i]);
> --	if(nblk <= EXT2_IND_BLOCK)
> --		return;
> --	swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
> --	if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
> --		return;
> --	/* See comment in swap_goodblocks */
> --	assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
> --	swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
> --	for(i = 0; i < BLOCKSIZE/4; i++)
> --		if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
> --			swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
> --	if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
> --		return;
> --	/* Adding support for triple indirection */
> --	b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
> --	swap_block((uint8 *)b);
> --	for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
> --		b2 = (uint32*)get_blk(fs,b[i]); 
> --		swap_block((uint8 *)b2);
> --		for(j=0; j<BLOCKSIZE/4;j++) {
> --			if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + 
> --				     (BLOCKSIZE/4)*(BLOCKSIZE/4) + 
> --				     i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + 
> --				     j*(BLOCKSIZE/4)) ) 
> --			  swap_block(get_blk(fs,b2[j]));
> --			else {
> --			  done = 1;
> --			  break;
> --			}
> --		}
> --	}
> --	return;
> --}
> -+	fs = malloc(sizeof(*fs));
> -+	if (!fs)
> -+		error_msg_and_die("not enough memory for filesystem");
> -+	memset(fs, 0, sizeof(*fs));
> -+	fs->swapit = swapit;
> -+	cache_init(&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed);
> -+	cache_init(&fs->gds, MAX_FREE_CACHE_GDS, gd_elem_val, gd_freed);
> -+	cache_init(&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS,
> -+		   blkmap_elem_val, blkmap_freed);
> -+	cache_init(&fs->inodes, MAX_FREE_CACHE_INODES,
> -+		   inode_elem_val, inode_freed);
> -+	fs->hdlink_cnt = HDLINK_CNT;
> -+	fs->hdlinks.hdl = calloc(sizeof(struct hdlink_s), fs->hdlink_cnt);
> -+	if (!fs->hdlinks.hdl)
> -+		error_msg_and_die("Not enough memory");
> -+	fs->hdlinks.count = 0 ;
> - 
> --// endianness swap of the whole filesystem
> --static void
> --swap_goodfs(filesystem *fs)
> --{
> --	uint32 i;
> --	for(i = 1; i < fs->sb.s_inodes_count; i++)
> --	{
> --		inode *nod = get_nod(fs, i);
> --		if(nod->i_mode & FM_IFDIR)
> --		{
> --			blockwalker bw;
> --			uint32 bk;
> --			init_bw(&bw);
> --			while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
> --			{
> --				directory *d;
> --				uint8 *b;
> --				b = get_blk(fs, bk);
> --				for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + swab16(d->d_rec_len)))
> --					swap_dir(d);
> --			}
> --		}
> --		swap_goodblocks(fs, nod);
> --		swap_nod(nod);
> --	}
> --	for(i=0;i<GRP_NBGROUPS(fs);i++)
> --		swap_gd(&(fs->gd[i]));
> --	swap_sb(&fs->sb);
> -+	if (strcmp(fname, "-") == 0)
> -+		fs->f = tmpfile();
> -+	else if (srcfile) {
> -+		if (fstat(fileno(srcfile), &srcstat))
> -+			perror_msg_and_die("fstat srcfile");
> -+		if (stat(fname, &dststat) == 0
> -+		    && srcstat.st_ino == dststat.st_ino
> -+		    && srcstat.st_dev == dststat.st_dev)
> -+		  {
> -+			// source and destination are the same file, don't
> -+			// truncate or copy, just use the file.
> -+			fs->f = fopen(fname, "r+b");
> -+		} else {
> -+			fs->f = fopen(fname, "w+b");
> -+			if (fs->f)
> -+				copy_file(fs, fs->f, srcfile, nbblocks);
> -+		}
> -+	} else
> -+		fs->f = fopen(fname, "w+b");
> -+	if (!fs->f)
> -+		perror_msg_and_die("opening %s", fname);
> -+	return fs;
> - }
> - 
> -+/* Make sure the output file is the right size */
> - static void
> --swap_badfs(filesystem *fs)
> -+set_file_size(filesystem *fs)
> - {
> --	uint32 i;
> --	swap_sb(&fs->sb);
> --	for(i=0;i<GRP_NBGROUPS(fs);i++)
> --		swap_gd(&(fs->gd[i]));
> --	for(i = 1; i < fs->sb.s_inodes_count; i++)
> --	{
> --		inode *nod = get_nod(fs, i);
> --		swap_nod(nod);
> --		swap_badblocks(fs, nod);
> --		if(nod->i_mode & FM_IFDIR)
> --		{
> --			blockwalker bw;
> --			uint32 bk;
> --			init_bw(&bw);
> --			while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
> --			{
> --				directory *d;
> --				uint8 *b;
> --				b = get_blk(fs, bk);
> --				for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
> --					swap_dir(d);
> --			}
> --		}
> --	}
> -+	if (ftruncate(fileno(fs->f),
> -+		      ((off_t) fs->sb->s_blocks_count) * BLOCKSIZE))
> -+		perror_msg_and_die("set_file_size: ftruncate");
> - }
> - 
> - // initialize an empty filesystem
> - static filesystem *
> --init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp)
> -+init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes,
> -+	uint32 fs_timestamp, uint32 creator_os, int swapit, char *fname)
> - {
> - 	uint32 i;
> - 	filesystem *fs;
> --	directory *d;
> --	uint8 * b;
> -+	dirwalker dw;
> - 	uint32 nod, first_block;
> - 	uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks,
> - 		free_blocks_per_group,nbblocks_per_group,min_nbgroups;
> -@@ -1906,6 +2545,11 @@
> - 	uint32 j;
> - 	uint8 *bbm,*ibm;
> - 	inode *itab0;
> -+	blk_info *bi;
> -+	nod_info *ni;
> -+	groupdescriptor *gd;
> -+	gd_info *gi;
> -+	inode_pos ipos;
> - 	
> - 	if(nbresrvd < 0)
> - 		error_msg_and_die("reserved blocks value is invalid. Note: options have changed, see --help or the man page.");
> -@@ -1919,10 +2563,14 @@
> - 	 */
> - 	min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
> - 
> -+	/* On filesystems with 1k block size, the bootloader area uses a full
> -+	 * block. For 2048 and up, the superblock can be fitted into block 0.
> -+	 */
> -+	first_block = (BLOCKSIZE == 1024);
> -+
> - 	/* nbblocks is the total number of blocks in the filesystem.
> - 	 * a block group can have no more than 8192 blocks.
> - 	 */
> --	first_block = (BLOCKSIZE == 1024);
> - 	nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP;
> - 	if(nbgroups < min_nbgroups) nbgroups = min_nbgroups;
> - 	nbblocks_per_group = rndup((nbblocks - first_block + nbgroups - 1)/nbgroups, 8);
> -@@ -1934,51 +2582,59 @@
> - 	gdsz = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
> - 	itblsz = nbinodes_per_group * sizeof(inode)/BLOCKSIZE;
> - 	overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz;
> --	if((uint32)nbblocks - 1 < overhead_per_group * nbgroups)
> --		error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
> --	free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/;
> -+	free_blocks = nbblocks - overhead_per_group*nbgroups - first_block;
> - 	free_blocks_per_group = nbblocks_per_group - overhead_per_group;
> -+	if(free_blocks < 0)
> -+		error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
> - 
> --	if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
> --		error_msg_and_die("not enough memory for filesystem");
> -+	fs = alloc_fs(swapit, fname, nbblocks, NULL);
> -+	fs->sb = calloc(1, SUPERBLOCK_SIZE);
> -+	if (!fs->sb)
> -+		error_msg_and_die("error allocating header memory");
> - 
> - 	// create the superblock for an empty filesystem
> --	fs->sb.s_inodes_count = nbinodes_per_group * nbgroups;
> --	fs->sb.s_blocks_count = nbblocks;
> --	fs->sb.s_r_blocks_count = nbresrvd;
> --	fs->sb.s_free_blocks_count = free_blocks;
> --	fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
> --	fs->sb.s_first_data_block = first_block;
> --	fs->sb.s_log_block_size = BLOCKSIZE >> 11;
> --	fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
> --	fs->sb.s_blocks_per_group = nbblocks_per_group;
> --	fs->sb.s_frags_per_group = nbblocks_per_group;
> --	fs->sb.s_inodes_per_group = nbinodes_per_group;
> --	fs->sb.s_wtime = fs_timestamp;
> --	fs->sb.s_magic = EXT2_MAGIC_NUMBER;
> --	fs->sb.s_lastcheck = fs_timestamp;
> -+	fs->sb->s_inodes_count = nbinodes_per_group * nbgroups;
> -+	fs->sb->s_blocks_count = nbblocks;
> -+	fs->sb->s_r_blocks_count = nbresrvd;
> -+	fs->sb->s_free_blocks_count = free_blocks;
> -+	fs->sb->s_free_inodes_count = fs->sb->s_inodes_count - EXT2_FIRST_INO + 1;
> -+	fs->sb->s_first_data_block = first_block;
> -+	fs->sb->s_log_block_size = BLOCKSIZE >> 11;
> -+	fs->sb->s_log_frag_size = BLOCKSIZE >> 11;
> -+	fs->sb->s_blocks_per_group = nbblocks_per_group;
> -+	fs->sb->s_frags_per_group = nbblocks_per_group;
> -+	fs->sb->s_inodes_per_group = nbinodes_per_group;
> -+	fs->sb->s_wtime = fs_timestamp;
> -+	fs->sb->s_magic = EXT2_MAGIC_NUMBER;
> -+	fs->sb->s_lastcheck = fs_timestamp;
> -+	fs->sb->s_creator_os = creator_os;
> -+
> -+	set_file_size(fs);
> - 
> - 	// set up groupdescriptors
> --	for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1;
> -+	for(i=0, bbmpos=first_block+1+gdsz, ibmpos=bbmpos+1, itblpos=ibmpos+1;
> - 		i<nbgroups;
> - 		i++, bbmpos+=nbblocks_per_group, ibmpos+=nbblocks_per_group, itblpos+=nbblocks_per_group)
> - 	{
> -+		gd = get_gd(fs, i, &gi);
> -+
> - 		if(free_blocks > free_blocks_per_group) {
> --			fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
> -+			gd->bg_free_blocks_count = free_blocks_per_group;
> - 			free_blocks -= free_blocks_per_group;
> - 		} else {
> --			fs->gd[i].bg_free_blocks_count = free_blocks;
> -+			gd->bg_free_blocks_count = free_blocks;
> - 			free_blocks = 0; // this is the last block group
> - 		}
> - 		if(i)
> --			fs->gd[i].bg_free_inodes_count = nbinodes_per_group;
> -+			gd->bg_free_inodes_count = nbinodes_per_group;
> - 		else
> --			fs->gd[i].bg_free_inodes_count = nbinodes_per_group -
> -+			gd->bg_free_inodes_count = nbinodes_per_group -
> - 							EXT2_FIRST_INO + 2;
> --		fs->gd[i].bg_used_dirs_count = 0;
> --		fs->gd[i].bg_block_bitmap = bbmpos;
> --		fs->gd[i].bg_inode_bitmap = ibmpos;
> --		fs->gd[i].bg_inode_table = itblpos;
> -+		gd->bg_used_dirs_count = 0;
> -+		gd->bg_block_bitmap = bbmpos;
> -+		gd->bg_inode_bitmap = ibmpos;
> -+		gd->bg_inode_table = itblpos;
> -+		put_gd(gi);
> - 	}
> - 
> - 	/* Mark non-filesystem blocks and inodes as allocated */
> -@@ -1984,110 +2640,143 @@
> - 	/* Mark non-filesystem blocks and inodes as allocated */
> - 	/* Mark system blocks and inodes as allocated         */
> - 	for(i = 0; i<nbgroups;i++) {
> --
> - 		/* Block bitmap */
> --		bbm = get_blk(fs,fs->gd[i].bg_block_bitmap);	
> -+		gd = get_gd(fs, i, &gi);
> -+		bbm = GRP_GET_GROUP_BBM(fs, gd, &bi);
> - 		//non-filesystem blocks
> --		for(j = fs->gd[i].bg_free_blocks_count
> -+		for(j = gd->bg_free_blocks_count
> - 		        + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++)
> - 			allocate(bbm, j); 
> - 		//system blocks
> - 		for(j = 1; j <= overhead_per_group; j++)
> - 			allocate(bbm, j); 
> --		
> -+		GRP_PUT_GROUP_BBM(bi);
> -+
> - 		/* Inode bitmap */
> --		ibm = get_blk(fs,fs->gd[i].bg_inode_bitmap);	
> -+		ibm = GRP_GET_GROUP_IBM(fs, gd, &bi);
> - 		//non-filesystem inodes
> --		for(j = fs->sb.s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
> -+		for(j = fs->sb->s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
> - 			allocate(ibm, j);
> - 
> - 		//system inodes
> - 		if(i == 0)
> - 			for(j = 1; j < EXT2_FIRST_INO; j++)
> - 				allocate(ibm, j);
> -+		GRP_PUT_GROUP_IBM(bi);
> -+		put_gd(gi);
> - 	}
> - 
> - 	// make root inode and directory
> - 	/* We have groups now. Add the root filesystem in group 0 */
> - 	/* Also increment the directory count for group 0 */
> --	fs->gd[0].bg_free_inodes_count--;
> --	fs->gd[0].bg_used_dirs_count = 1;
> --	itab0 = (inode *)get_blk(fs,fs->gd[0].bg_inode_table);
> --	itab0[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH; 
> --	itab0[EXT2_ROOT_INO-1].i_ctime = fs_timestamp;
> --	itab0[EXT2_ROOT_INO-1].i_mtime = fs_timestamp;
> --	itab0[EXT2_ROOT_INO-1].i_atime = fs_timestamp;
> --	itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
> --	itab0[EXT2_ROOT_INO-1].i_links_count = 2;
> --
> --	if(!(b = get_workblk()))
> --		error_msg_and_die("get_workblk() failed.");
> --	d = (directory*)b;
> --	d->d_inode = EXT2_ROOT_INO;
> --	d->d_rec_len = sizeof(directory)+4;
> --	d->d_name_len = 1;
> --	strcpy(d->d_name, ".");
> --	d = (directory*)(b + d->d_rec_len);
> --	d->d_inode = EXT2_ROOT_INO;
> --	d->d_rec_len = BLOCKSIZE - (sizeof(directory)+4);
> --	d->d_name_len = 2;
> --	strcpy(d->d_name, "..");
> --	extend_blk(fs, EXT2_ROOT_INO, b, 1);
> -+	gd = get_gd(fs, 0, &gi);
> -+	gd->bg_free_inodes_count--;
> -+	gd->bg_used_dirs_count = 1;
> -+	put_gd(gi);
> -+	itab0 = get_nod(fs, EXT2_ROOT_INO, &ni);
> -+	itab0->i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH;
> -+	itab0->i_ctime = fs_timestamp;
> -+	itab0->i_mtime = fs_timestamp;
> -+	itab0->i_atime = fs_timestamp;
> -+	itab0->i_size = BLOCKSIZE;
> -+	itab0->i_links_count = 2;
> -+	put_nod(ni);
> -+
> -+	new_dir(fs, EXT2_ROOT_INO, ".", 1, &dw);
> -+	shrink_dir(&dw, EXT2_ROOT_INO, "..", 2);
> -+	next_dir(&dw); // Force the data into the buffer
> -+	inode_pos_init(fs, &ipos, EXT2_ROOT_INO, INODE_POS_EXTEND, NULL);
> -+	extend_inode_blk(fs, &ipos, dir_data(&dw), 1);
> -+	inode_pos_finish(fs, &ipos);
> -+	put_dir(&dw);
> - 
> --	// make lost+found directory and reserve blocks
> --	if(fs->sb.s_r_blocks_count)
> -+	// make lost+found directory
> -+	if(fs->sb->s_r_blocks_count)
> - 	{
> --		nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
> -+		inode *node;
> -+		uint8 *b;
> -+
> -+		nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU,
> -+			       0, 0, fs_timestamp, fs_timestamp);
> -+		b = get_workblk();
> - 		memset(b, 0, BLOCKSIZE);
> - 		((directory*)b)->d_rec_len = BLOCKSIZE;
> --		/* We run into problems with e2fsck if directory lost+found grows
> --		 * bigger than this. Need to find out why this happens - sundar
> --		 */
> --		if (fs->sb.s_r_blocks_count > fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS ) 
> --			fs->sb.s_r_blocks_count = fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS;
> --		for(i = 1; i < fs->sb.s_r_blocks_count; i++)
> --			extend_blk(fs, nod, b, 1);
> --		get_nod(fs, nod)->i_size = fs->sb.s_r_blocks_count * BLOCKSIZE;
> -+		inode_pos_init(fs, &ipos, nod, INODE_POS_EXTEND, NULL);
> -+		// It is always 16 blocks to start out with
> -+		for(i = 1; i < 16; i++)
> -+			extend_inode_blk(fs, &ipos, b, 1);
> -+		inode_pos_finish(fs, &ipos);
> -+		free_workblk(b);
> -+		node = get_nod(fs, nod, &ni);
> -+		node->i_size = 16 * BLOCKSIZE;
> -+		put_nod(ni);
> - 	}
> --	free_workblk(b);
> - 
> - 	// administrative info
> --	fs->sb.s_state = 1;
> --	fs->sb.s_max_mnt_count = 20;
> -+	fs->sb->s_state = 1;
> -+	fs->sb->s_max_mnt_count = 20;
> - 
> - 	// options for me
> --	if(holes)
> --		fs->sb.s_reserved[200] |= OP_HOLES;
> -+	fs->holes = holes;
> - 	
> - 	return fs;
> - }
> - 
> - // loads a filesystem from disk
> - static filesystem *
> --load_fs(FILE * fh, int swapit)
> -+load_fs(FILE *fh, int swapit, char *fname)
> - {
> --	size_t fssize;
> -+	off_t fssize;
> - 	filesystem *fs;
> --	if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1))
> -+
> -+	if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftello(fh)) == -1))
> - 		perror_msg_and_die("input filesystem image");
> - 	rewind(fh);
> --	fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
> -+	if ((fssize % BLOCKSIZE) != 0)
> -+		error_msg_and_die("Input file not a multiple of block size");
> -+	fssize /= BLOCKSIZE;
> - 	if(fssize < 16) // totally arbitrary
> - 		error_msg_and_die("too small filesystem");
> --	if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
> --		error_msg_and_die("not enough memory for filesystem");
> --	if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
> --		perror_msg_and_die("input filesystem image");
> -+	fs = alloc_fs(swapit, fname, fssize, fh);
> -+
> -+	/* Read and check the superblock, then read the superblock
> -+	 * and all the group descriptors */
> -+	fs->sb = malloc(SUPERBLOCK_SIZE);
> -+	if (!fs->sb)
> -+		error_msg_and_die("error allocating header memory");
> -+	if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET))
> -+		perror_msg_and_die("fseek");
> -+	if (fread(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1)
> -+		perror_msg_and_die("fread filesystem image superblock");
> - 	if(swapit)
> --		swap_badfs(fs);
> --	if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
> -+		swap_sb(fs->sb);
> -+
> -+	if((fs->sb->s_rev_level > 1) || (fs->sb->s_magic != EXT2_MAGIC_NUMBER))
> - 		error_msg_and_die("not a suitable ext2 filesystem");
> -+	if (fs->sb->s_rev_level > 0) {
> -+		if (fs->sb->s_first_ino != EXT2_GOOD_OLD_FIRST_INO)
> -+			error_msg_and_die("First inode incompatible");
> -+		if (fs->sb->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE)
> -+			error_msg_and_die("inode size incompatible");
> -+		if (fs->sb->s_feature_compat)
> -+			error_msg_and_die("Unsupported compat features");
> -+		if (fs->sb->s_feature_incompat)
> -+			error_msg_and_die("Unsupported incompat features");
> -+		if (fs->sb->s_feature_ro_compat
> -+		    & ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
> -+			error_msg_and_die("Unsupported ro compat features");
> -+	}
> -+
> -+	set_file_size(fs);
> - 	return fs;
> - }
> - 
> - static void
> - free_fs(filesystem *fs)
> - {
> -+	free(fs->hdlinks.hdl);
> -+	fclose(fs->f);
> -+	free(fs->sb);
> - 	free(fs);
> - }
> - 
> -@@ -2123,16 +2812,23 @@
> - {
> - 	blockwalker bw;
> - 	uint32 bk;
> --	int32 fsize = get_nod(fs, nod)->i_size;
> -+	nod_info *ni;
> -+	inode *node = get_nod(fs, nod, &ni);
> -+	int32 fsize = node->i_size;
> -+	blk_info *bi;
> -+
> - 	init_bw(&bw);
> - 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
> - 	{
> - 		if(fsize <= 0)
> - 			error_msg_and_die("wrong size while saving inode %d", nod);
> --		if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
> -+		if(fwrite(get_blk(fs, bk, &bi),
> -+			  (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
> - 			error_msg_and_die("error while saving inode %d", nod);
> -+		put_blk(bi);
> - 		fsize -= BLOCKSIZE;
> - 	}
> -+	put_nod(ni);
> - }
> - 
> - 
> -@@ -2141,8 +2837,11 @@
> - print_dev(filesystem *fs, uint32 nod)
> - {
> - 	int minor, major;
> --	minor = ((uint8*)get_nod(fs, nod)->i_block)[0];
> --	major = ((uint8*)get_nod(fs, nod)->i_block)[1];
> -+	nod_info *ni;
> -+	inode *node = get_nod(fs, nod, &ni);
> -+	minor = ((uint8*)node->i_block)[0];
> -+	major = ((uint8*)node->i_block)[1];
> -+	put_nod(ni);
> - 	printf("major: %d, minor: %d\n", major, minor);
> - }
> - 
> -@@ -2157,17 +2856,15 @@
> - 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
> - 	{
> - 		directory *d;
> --		uint8 *b;
> --		b = get_blk(fs, bk);
> --		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
> -+		dirwalker dw;
> -+		for (d = get_dir(fs, bk, &dw); d; d = next_dir(&dw))
> - 			if(d->d_inode)
> - 			{
> --				int i;
> - 				printf("entry '");
> --				for(i = 0; i < d->d_name_len; i++)
> --					putchar(d->d_name[i]);
> -+				fwrite(dir_name(&dw), 1, d->d_name_len, stdout);
> - 				printf("' (inode %d): rec_len: %d (name_len: %d)\n", d->d_inode, d->d_rec_len, d->d_name_len);
> - 			}
> -+		put_dir(&dw);
> - 	}
> - }
> - 
> -@@ -2175,14 +2872,18 @@
> - static void
> - print_link(filesystem *fs, uint32 nod)
> - {
> --	if(!get_nod(fs, nod)->i_blocks)
> --		printf("links to '%s'\n", (char*)get_nod(fs, nod)->i_block);
> -+	nod_info *ni;
> -+	inode *node = get_nod(fs, nod, &ni);
> -+
> -+	if(!node->i_blocks)
> -+		printf("links to '%s'\n", (char*)node->i_block);
> - 	else
> - 	{
> - 		printf("links to '");
> - 		write_blocks(fs, nod, stdout);
> - 		printf("'\n");
> - 	}
> -+	put_nod(ni);
> - }
> - 
> - // make a ls-like printout of permissions
> -@@ -2251,8 +2952,13 @@
> - {
> - 	char *s;
> - 	char perms[11];
> --	if(!get_nod(fs, nod)->i_mode)
> --		return;
> -+	nod_info *ni;
> -+	inode *node = get_nod(fs, nod, &ni);
> -+	blk_info *bi;
> -+	gd_info *gi;
> -+
> -+	if(!node->i_mode)
> -+		goto out;
> - 	switch(nod)
> - 	{
> - 		case EXT2_BAD_INO:
> -@@ -2274,15 +2980,18 @@
> - 		default:
> - 			s = (nod >= EXT2_FIRST_INO) ? "normal" : "unknown reserved"; 
> - 	}
> --	printf("inode %d (%s, %d links): ", nod, s, get_nod(fs, nod)->i_links_count);
> --	if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod)))
> -+	printf("inode %d (%s, %d links): ", nod, s, node->i_links_count);
> -+	if(!allocated(GRP_GET_INODE_BITMAP(fs,nod,&bi,&gi), GRP_IBM_OFFSET(fs,nod)))
> - 	{
> -+		GRP_PUT_INODE_BITMAP(bi,gi);
> - 		printf("unallocated\n");
> --		return;
> -+		goto out;
> - 	}
> --	make_perms(get_nod(fs, nod)->i_mode, perms);
> --	printf("%s,  size: %d byte%s (%d block%s)\n", perms, plural(get_nod(fs, nod)->i_size), plural(get_nod(fs, nod)->i_blocks / INOBLK));
> --	switch(get_nod(fs, nod)->i_mode & FM_IFMT)
> -+	GRP_PUT_INODE_BITMAP(bi,gi);
> -+	make_perms(node->i_mode, perms);
> -+	printf("%s,  size: %d byte%s (%d block%s)\n", perms,
> -+	       plural(node->i_size), plural(node->i_blocks / INOBLK));
> -+	switch(node->i_mode & FM_IFMT)
> - 	{
> - 		case FM_IFSOCK:
> - 			list_blocks(fs, nod);
> -@@ -2310,6 +3019,8 @@
> - 			list_blocks(fs, nod);
> - 	}
> - 	printf("Done with inode %d\n",nod);
> -+out:
> -+	put_nod(ni);
> - }
> - 
> - // describes various fields in a filesystem
> -@@ -2317,49 +3028,65 @@
> - print_fs(filesystem *fs)
> - {
> - 	uint32 i;
> -+	blk_info *bi;
> -+	groupdescriptor *gd;
> -+	gd_info *gi;
> - 	uint8 *ibm;
> - 
> - 	printf("%d blocks (%d free, %d reserved), first data block: %d\n",
> --	       fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
> --	       fs->sb.s_r_blocks_count, fs->sb.s_first_data_block);
> --	printf("%d inodes (%d free)\n", fs->sb.s_inodes_count,
> --	       fs->sb.s_free_inodes_count);
> -+	       fs->sb->s_blocks_count, fs->sb->s_free_blocks_count,
> -+	       fs->sb->s_r_blocks_count, fs->sb->s_first_data_block);
> -+	printf("%d inodes (%d free)\n", fs->sb->s_inodes_count,
> -+	       fs->sb->s_free_inodes_count);
> - 	printf("block size = %d, frag size = %d\n",
> --	       fs->sb.s_log_block_size ? (fs->sb.s_log_block_size << 11) : 1024,
> --	       fs->sb.s_log_frag_size ? (fs->sb.s_log_frag_size << 11) : 1024);
> -+	       fs->sb->s_log_block_size ? (fs->sb->s_log_block_size << 11) : 1024,
> -+	       fs->sb->s_log_frag_size ? (fs->sb->s_log_frag_size << 11) : 1024);
> - 	printf("number of groups: %d\n",GRP_NBGROUPS(fs));
> - 	printf("%d blocks per group,%d frags per group,%d inodes per group\n",
> --	     fs->sb.s_blocks_per_group, fs->sb.s_frags_per_group,
> --	     fs->sb.s_inodes_per_group);
> -+	     fs->sb->s_blocks_per_group, fs->sb->s_frags_per_group,
> -+	     fs->sb->s_inodes_per_group);
> - 	printf("Size of inode table: %d blocks\n",
> --		(int)(fs->sb.s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
> -+		(int)(fs->sb->s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
> - 	for (i = 0; i < GRP_NBGROUPS(fs); i++) {
> - 		printf("Group No: %d\n", i+1);
> -+		gd = get_gd(fs, i, &gi);
> - 		printf("block bitmap: block %d,inode bitmap: block %d, inode table: block %d\n",
> --		     fs->gd[i].bg_block_bitmap, fs->gd[i].bg_inode_bitmap,
> --		     fs->gd[i].bg_inode_table);
> -+		     gd->bg_block_bitmap,
> -+		     gd->bg_inode_bitmap,
> -+		     gd->bg_inode_table);
> - 		printf("block bitmap allocation:\n");
> --		print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group);
> -+		print_bm(GRP_GET_GROUP_BBM(fs, gd, &bi),fs->sb->s_blocks_per_group);
> -+		GRP_PUT_GROUP_BBM(bi);
> - 		printf("inode bitmap allocation:\n");
> --		ibm = GRP_GET_GROUP_IBM(fs, i);
> --		print_bm(ibm, fs->sb.s_inodes_per_group);
> --		for (i = 1; i <= fs->sb.s_inodes_per_group; i++)
> -+		ibm = GRP_GET_GROUP_IBM(fs, gd, &bi);
> -+		print_bm(ibm, fs->sb->s_inodes_per_group);
> -+		for (i = 1; i <= fs->sb->s_inodes_per_group; i++)
> - 			if (allocated(ibm, i))
> - 				print_inode(fs, i);
> -+		GRP_PUT_GROUP_IBM(bi);
> -+		put_gd(gi);
> - 	}
> - }
> - 
> - static void
> --dump_fs(filesystem *fs, FILE * fh, int swapit)
> -+finish_fs(filesystem *fs)
> - {
> --	uint32 nbblocks = fs->sb.s_blocks_count;
> --	fs->sb.s_reserved[200] = 0;
> --	if(swapit)
> --		swap_goodfs(fs);
> --	if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
> --		perror_msg_and_die("output filesystem image");
> --	if(swapit)
> --		swap_badfs(fs);
> -+	if (cache_flush(&fs->inodes))
> -+		error_msg_and_die("entry mismatch on inode cache flush");
> -+	if (cache_flush(&fs->blkmaps))
> -+		error_msg_and_die("entry mismatch on blockmap cache flush");
> -+	if (cache_flush(&fs->gds))
> -+		error_msg_and_die("entry mismatch on gd cache flush");
> -+	if (cache_flush(&fs->blks))
> -+		error_msg_and_die("entry mismatch on block cache flush");
> -+	if(fs->swapit)
> -+		swap_sb(fs->sb);
> -+	if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET))
> -+		perror_msg_and_die("fseek");
> -+	if(fwrite(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1)
> -+		perror_msg_and_die("output filesystem superblock");
> -+	if(fs->swapit)
> -+		swap_sb(fs->sb);
> - }
> - 
> - static void
> -@@ -2419,10 +3146,12 @@
> - 	"  -x, --starting-image <image>\n"
> - 	"  -d, --root <directory>\n"
> - 	"  -D, --devtable <file>\n"
> -+	"  -B, --block-size <bytes>\n"
> - 	"  -b, --size-in-blocks <blocks>\n"
> - 	"  -i, --bytes-per-inode <bytes per inode>\n"
> - 	"  -N, --number-of-inodes <number of inodes>\n"
> - 	"  -m, --reserved-percentage <percentage of blocks to reserve>\n"
> -+	"  -o, --creator-os <os>      'linux' (default), 'hurd', 'freebsd' or number.\n"
> - 	"  -g, --block-map <path>     Generate a block map file for this path.\n"
> - 	"  -e, --fill-value <value>   Fill unallocated blocks with value.\n"
> - 	"  -z, --allow-holes          Allow files with holes.\n"
> -@@ -2444,15 +3173,34 @@
> - extern char* optarg;
> - extern int optind, opterr, optopt;
> - 
> -+// parse the value for -o <os>
> -+int
> -+lookup_creator_os(const char *name)
> -+{
> -+        if (isdigit (*name))
> -+                return atoi(name);
> -+        else if (strcasecmp(name, "linux") == 0)
> -+                return EXT2_OS_LINUX;
> -+        else if (strcasecmp(name, "GNU") == 0 || strcasecmp(name, "hurd") == 0)
> -+                return EXT2_OS_HURD;
> -+        else if (strcasecmp(name, "freebsd") == 0)
> -+                return EXT2_OS_FREEBSD;
> -+        else if (strcasecmp(name, "lites") == 0)
> -+                return EXT2_OS_LITES;
> -+        else
> -+                return EXT2_OS_LINUX;
> -+}
> -+
> - int
> - main(int argc, char **argv)
> - {
> --	int nbblocks = -1;
> -+	long long nbblocks = -1;
> - 	int nbinodes = -1;
> - 	int nbresrvd = -1;
> - 	float bytes_per_inode = -1;
> - 	float reserved_frac = -1;
> - 	int fs_timestamp = -1;
> -+	int creator_os = CREATOR_OS;
> - 	char * fsout = "-";
> - 	char * fsin = 0;
> - 	char * dopt[MAX_DOPT];
> -@@ -2466,6 +3214,7 @@
> - 	int squash_perms = 0;
> - 	uint16 endian = 1;
> - 	int bigendian = !*(char*)&endian;
> -+	char *volumelabel = NULL;
> - 	filesystem *fs;
> - 	int i;
> - 	int c;
> -@@ -2476,13 +3225,16 @@
> - 	  { "starting-image",	required_argument,	NULL, 'x' },
> - 	  { "root",		required_argument,	NULL, 'd' },
> - 	  { "devtable",		required_argument,	NULL, 'D' },
> -+	  { "block-size",	required_argument,	NULL, 'B' },
> - 	  { "size-in-blocks",	required_argument,	NULL, 'b' },
> - 	  { "bytes-per-inode",	required_argument,	NULL, 'i' },
> - 	  { "number-of-inodes",	required_argument,	NULL, 'N' },
> -+	  { "volume-label",     required_argument,      NULL, 'L' },
> - 	  { "reserved-percentage", required_argument,	NULL, 'm' },
> -+	  { "creator-os",	required_argument,	NULL, 'o' },
> - 	  { "block-map",	required_argument,	NULL, 'g' },
> - 	  { "fill-value",	required_argument,	NULL, 'e' },
> --	  { "allow-holes",	no_argument, 		NULL, 'z' },
> -+	  { "allow-holes",	no_argument,		NULL, 'z' },
> - 	  { "faketime",		no_argument,		NULL, 'f' },
> - 	  { "squash",		no_argument,		NULL, 'q' },
> - 	  { "squash-uids",	no_argument,		NULL, 'U' },
> -@@ -2495,11 +3247,11 @@
> - 
> - 	app_name = argv[0];
> - 
> --	while((c = getopt_long(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
> -+	while((c = getopt_long(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
> - #else
> - 	app_name = argv[0];
> - 
> --	while((c = getopt(argc, argv,      "x:d:D:b:i:N:m:g:e:zfqUPhVv")) != EOF) {
> -+	while((c = getopt(argc, argv,      "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv")) != EOF) {
> - #endif /* HAVE_GETOPT_LONG */
> - 		switch(c)
> - 		{
> -@@ -2510,6 +3262,9 @@
> - 			case 'D':
> - 				dopt[didx++] = optarg;
> - 				break;
> -+			case 'B':
> -+				blocksize = SI_atof(optarg);
> -+				break;
> - 			case 'b':
> - 				nbblocks = SI_atof(optarg);
> - 				break;
> -@@ -2519,9 +3274,15 @@
> - 			case 'N':
> - 				nbinodes = SI_atof(optarg);
> - 				break;
> -+			case 'L':
> -+				volumelabel = optarg;
> -+				break;
> - 			case 'm':
> - 				reserved_frac = SI_atof(optarg) / 100;
> - 				break;
> -+			case 'o':
> -+				creator_os = lookup_creator_os(optarg);
> -+				break;
> - 			case 'g':
> - 				gopt[gidx++] = optarg;
> - 				break;
> -@@ -2565,21 +3326,21 @@
> - 		error_msg_and_die("Not enough arguments. Try --help or else see the man page.");
> - 	fsout = argv[optind];
> - 
> --	hdlinks.hdl = (struct hdlink_s *)malloc(hdlink_cnt * sizeof(struct hdlink_s));
> --	if (!hdlinks.hdl)
> --		error_msg_and_die("Not enough memory");
> --	hdlinks.count = 0 ;
> -+	if(blocksize != 1024 && blocksize != 2048 && blocksize != 4096)
> -+		error_msg_and_die("Valid block sizes: 1024, 2048 or 4096.");
> -+	if(creator_os < 0)
> -+		error_msg_and_die("Creator OS unknown.");
> - 
> - 	if(fsin)
> - 	{
> - 		if(strcmp(fsin, "-"))
> - 		{
> - 			FILE * fh = xfopen(fsin, "rb");
> --			fs = load_fs(fh, bigendian);
> -+			fs = load_fs(fh, bigendian, fsout);
> - 			fclose(fh);
> - 		}
> - 		else
> --			fs = load_fs(stdin, bigendian);
> -+			fs = load_fs(stdin, bigendian, fsout);
> - 	}
> - 	else
> - 	{
> -@@ -2609,16 +3370,29 @@
> - 		}
> - 		if(fs_timestamp == -1)
> - 			fs_timestamp = time(NULL);
> --		fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp);
> -+		fs = init_fs(nbblocks, nbinodes, nbresrvd, holes,
> -+			     fs_timestamp, creator_os, bigendian, fsout);
> - 	}
> -+	if (volumelabel != NULL)
> -+		strncpy((char *)fs->sb->s_volume_name, volumelabel,
> -+			sizeof(fs->sb->s_volume_name));
> - 	
> - 	populate_fs(fs, dopt, didx, squash_uids, squash_perms, fs_timestamp, NULL);
> - 
> - 	if(emptyval) {
> - 		uint32 b;
> --		for(b = 1; b < fs->sb.s_blocks_count; b++)
> --			if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b),GRP_BBM_OFFSET(fs,b)))
> --				memset(get_blk(fs, b), emptyval, BLOCKSIZE);
> -+		for(b = 1; b < fs->sb->s_blocks_count; b++) {
> -+			blk_info *bi;
> -+			gd_info *gi;
> -+			if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b,&bi,&gi),
> -+				      GRP_BBM_OFFSET(fs,b))) {
> -+				blk_info *bi2;
> -+				memset(get_blk(fs, b, &bi2), emptyval,
> -+				       BLOCKSIZE);
> -+				put_blk(bi2);
> -+			}
> -+			GRP_PUT_BLOCK_BITMAP(bi,gi);
> -+		}
> - 	}
> - 	if(verbose)
> - 		print_fs(fs);
> -@@ -2628,24 +3402,22 @@
> - 		char fname[MAX_FILENAME];
> - 		char *p;
> - 		FILE *fh;
> -+		nod_info *ni;
> - 		if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i])))
> - 			error_msg_and_die("path %s not found in filesystem", gopt[i]);
> - 		while((p = strchr(gopt[i], '/')))
> - 			*p = '_';
> - 		SNPRINTF(fname, MAX_FILENAME-1, "%s.blk", gopt[i]);
> - 		fh = xfopen(fname, "wb");
> --		fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
> -+		fprintf(fh, "%d:", get_nod(fs, nod, &ni)->i_size);
> -+		put_nod(ni);
> - 		flist_blocks(fs, nod, fh);
> - 		fclose(fh);
> - 	}
> --	if(strcmp(fsout, "-"))
> --	{
> --		FILE * fh = xfopen(fsout, "wb");
> --		dump_fs(fs, fh, bigendian);
> --		fclose(fh);
> --	}
> --	else
> --		dump_fs(fs, stdout, bigendian);
> -+	finish_fs(fs);
> -+	if(strcmp(fsout, "-") == 0)
> -+		copy_file(fs, stdout, fs->f, fs->sb->s_blocks_count);
> -+
> - 	free_fs(fs);
> - 	return 0;
> - }
> -Index: genext2fs-1.4.1/cache.h
> -===================================================================
> ---- /dev/null
> -+++ genext2fs-1.4.1/cache.h
> -@@ -0,0 +1,128 @@
> -+#ifndef __CACHE_H__
> -+#define __CACHE_H__
> -+
> -+#include "list.h"
> -+
> -+#define CACHE_LISTS 256
> -+
> -+typedef struct
> -+{
> -+    list_elem link;
> -+    list_elem lru_link;
> -+} cache_link;
> -+
> -+typedef struct
> -+{
> -+    /* LRU list holds unused items */
> -+    unsigned int lru_entries;
> -+    list_elem lru_list;
> -+    unsigned int max_free_entries;
> -+
> -+    unsigned int entries;
> -+    list_elem lists[CACHE_LISTS];
> -+    unsigned int (*elem_val)(cache_link *elem);
> -+    void (*freed)(cache_link *elem);
> -+} listcache;
> -+
> -+static inline void
> -+cache_add(listcache *c, cache_link *elem)
> -+{
> -+    unsigned int hash = c->elem_val(elem) % CACHE_LISTS;
> -+    int delcount = c->lru_entries - c->max_free_entries;
> -+
> -+    if (delcount > 0) {
> -+        /* Delete some unused items. */
> -+        list_elem *lru, *next;
> -+        cache_link *l;
> -+        list_for_each_elem_safe(&c->lru_list, lru, next) {
> -+            l = container_of(lru, cache_link, lru_link);
> -+            list_del(lru);
> -+            list_del(&l->link);
> -+            c->entries--;
> -+            c->lru_entries--;
> -+            c->freed(l);
> -+            delcount--;
> -+            if (delcount <= 0)
> -+                break;
> -+        }
> -+    }
> -+
> -+    c->entries++;
> -+    list_item_init(&elem->lru_link); /* Mark it not in the LRU list */
> -+    list_add_after(&c->lists[hash], &elem->link);
> -+}
> -+
> -+static inline void
> -+cache_item_set_unused(listcache *c, cache_link *elem)
> -+{
> -+    list_add_before(&c->lru_list, &elem->lru_link);
> -+    c->lru_entries++;
> -+}
> -+
> -+static inline cache_link *
> -+cache_find(listcache *c, unsigned int val)
> -+{
> -+    unsigned int hash = val % CACHE_LISTS;
> -+    list_elem *elem;
> -+
> -+    list_for_each_elem(&c->lists[hash], elem) {
> -+        cache_link *l = container_of(elem, cache_link, link);
> -+        if (c->elem_val(l) == val) {
> -+            if (!list_empty(&l->lru_link)) {
> -+                /* It's in the unused list, remove it. */
> -+                list_del(&l->lru_link);
> -+                list_item_init(&l->lru_link);
> -+                c->lru_entries--;
> -+            }
> -+            return l;
> -+        }
> -+    }
> -+    return NULL;
> -+}
> -+
> -+static inline int
> -+cache_flush(listcache *c)
> -+{
> -+    list_elem *elem, *next;
> -+    cache_link *l;
> -+    int i;
> -+
> -+    list_for_each_elem_safe(&c->lru_list, elem, next) {
> -+        l = container_of(elem, cache_link, lru_link);
> -+        list_del(elem);
> -+        list_del(&l->link);
> -+        c->entries--;
> -+        c->lru_entries--;
> -+        c->freed(l);
> -+    }
> -+
> -+    for (i = 0; i < CACHE_LISTS; i++) {
> -+        list_for_each_elem_safe(&c->lists[i], elem, next) {
> -+            l = container_of(elem, cache_link, link);
> -+            list_del(&l->link);
> -+            c->entries--;
> -+            c->freed(l);
> -+        }
> -+    }
> -+
> -+    return c->entries || c->lru_entries;
> -+}
> -+
> -+static inline void
> -+cache_init(listcache *c, unsigned int max_free_entries,
> -+       unsigned int (*elem_val)(cache_link *elem),
> -+       void (*freed)(cache_link *elem))
> -+{
> -+    int i;
> -+
> -+    c->entries = 0;
> -+    c->lru_entries = 0;
> -+    c->max_free_entries = max_free_entries;
> -+    list_init(&c->lru_list);
> -+    for (i = 0; i < CACHE_LISTS; i++)
> -+        list_init(&c->lists[i]);
> -+    c->elem_val = elem_val;
> -+    c->freed = freed;
> -+}
> -+
> -+#endif /* __CACHE_H__ */
> -Index: genext2fs-1.4.1/list.h
> -===================================================================
> ---- /dev/null
> -+++ genext2fs-1.4.1/list.h
> -@@ -0,0 +1,78 @@
> -+#ifndef __LIST_H__
> -+#define __LIST_H__
> -+
> -+#if STDC_HEADERS
> -+# include <stdlib.h>
> -+# include <stddef.h>
> -+#else
> -+# if HAVE_STDLIB_H
> -+#  include <stdlib.h>
> -+# endif
> -+# if HAVE_STDDEF_H
> -+#  include <stddef.h>
> -+# endif
> -+#endif
> -+
> -+#ifndef offsetof
> -+#define offsetof(st, m) \
> -+     ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
> -+#endif
> -+
> -+#define container_of(ptr, type, member) ({ \
> -+                const typeof( ((type *)0)->member ) *__mptr = (ptr); \
> -+                (type *)( (char *)__mptr - offsetof(type,member) );})
> -+
> -+typedef struct list_elem
> -+{
> -+    struct list_elem *next;
> -+    struct list_elem *prev;
> -+} list_elem;
> -+
> -+static inline void list_init(list_elem *list)
> -+{
> -+    list->next = list;
> -+    list->prev = list;
> -+}
> -+
> -+static inline void list_add_after(list_elem *pos, list_elem *elem)
> -+{
> -+    elem->next = pos->next;
> -+    elem->prev = pos;
> -+    pos->next->prev = elem;
> -+    pos->next = elem;
> -+}
> -+
> -+static inline void list_add_before(list_elem *pos, list_elem *elem)
> -+{
> -+    elem->prev = pos->prev;
> -+    elem->next = pos;
> -+    pos->prev->next = elem;
> -+    pos->prev = elem;
> -+}
> -+
> -+static inline void list_del(list_elem *elem)
> -+{
> -+    elem->next->prev = elem->prev;
> -+    elem->prev->next = elem->next;
> -+}
> -+
> -+static inline void list_item_init(list_elem *elem)
> -+{
> -+    elem->next = elem;
> -+    elem->prev = elem;
> -+}
> -+
> -+static inline int list_empty(list_elem *elem)
> -+{
> -+    return elem->next == elem;
> -+}
> -+
> -+#define list_for_each_elem(list, curr)            \
> -+    for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next)
> -+
> -+#define list_for_each_elem_safe(list, curr, next)    \
> -+    for ((curr) = (list)->next, (next) = (curr)->next;    \
> -+         (curr) != (list);                    \
> -+         (curr) = (next), (next) = (curr)->next)
> -+
> -+#endif /* __LIST_H__ */
> diff --git a/package/genext2fs/genext2fs.hash b/package/genext2fs/genext2fs.hash
> index add44c4c4e..20bb1c641b 100644
> --- a/package/genext2fs/genext2fs.hash
> +++ b/package/genext2fs/genext2fs.hash
> @@ -1,3 +1,3 @@
>  # Locally computed:
> -sha256  404dbbfa7a86a6c3de8225c8da254d026b17fd288e05cec4df2cc7e1f4feecfc  genext2fs-1.4.1.tar.gz
> +sha256  d3861e4fe89131bd21fbd25cf0b683b727b5c030c4c336fadcd738ada830aab0  genext2fs-1.5.0.tar.gz
>  sha256  32b1062f7da84967e7019d01ab805935caa7ab7321a7ced0e30ebe75e5df1670  COPYING
> diff --git a/package/genext2fs/genext2fs.mk b/package/genext2fs/genext2fs.mk
> index dd907c8b12..adfa412e66 100644
> --- a/package/genext2fs/genext2fs.mk
> +++ b/package/genext2fs/genext2fs.mk
> @@ -4,10 +4,12 @@
>  #
>  ################################################################################
>  
> -GENEXT2FS_VERSION = 1.4.1
> -GENEXT2FS_SITE = http://downloads.sourceforge.net/project/genext2fs/genext2fs/$(GENEXT2FS_VERSION)
> +GENEXT2FS_VERSION = 1.5.0
> +GENEXT2FS_SITE = $(call github,bestouff,genext2fs,v$(GENEXT2FS_VERSION))
>  GENEXT2FS_LICENSE = GPL-2.0
>  GENEXT2FS_LICENSE_FILES = COPYING
> +# From git
> +GENEXT2FS_AUTORECONF = YES
>  
>  $(eval $(autotools-package))
>  $(eval $(host-autotools-package))
>
stsp April 15, 2021, 10:32 p.m. UTC | #2
15.04.2021 22:20, Arnout Vandecappelle пишет:
> On 14/04/2021 23:44, Fabrice Fontaine wrote:
>> - Retrieve latest version from github
>> - Drop patch (already in version)
>>
>> Fixes:
>>   - https://bugs.buildroot.org/show_bug.cgi?id=13741
>>
>> Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
>
>   Applied to master, thanks.
>
>   I guess for stable, the patch [1] should be backported rather than bumping. But
> is it worth spending effort on that for a borderline package like genext2fs?
I tried really hard (before
reporting the above bug)
to find the equivalent
functionality in mkfs.ext[24],
and I think its simply not there.
I think only genext2fs allows
to set the perms for the root
node, so I am not sure why it
is borderline.
Are there any alternatives?
Buildroot currently creates
only the rootfs image, and
for that the default root node
perms are fine. But once you
want to create the user partition,
I think genext2fs is the only
solution.
Please let me know if this is
wrong.
Arnout Vandecappelle April 16, 2021, 7:33 a.m. UTC | #3
On 16/04/2021 00:32, stsp wrote:
> 15.04.2021 22:20, Arnout Vandecappelle пишет:
>> On 14/04/2021 23:44, Fabrice Fontaine wrote:
>>> - Retrieve latest version from github
>>> - Drop patch (already in version)
>>>
>>> Fixes:
>>>   - https://bugs.buildroot.org/show_bug.cgi?id=13741
>>>
>>> Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
>>
>>   Applied to master, thanks.
>>
>>   I guess for stable, the patch [1] should be backported rather than bumping. But
>> is it worth spending effort on that for a borderline package like genext2fs?
> I tried really hard (before
> reporting the above bug)
> to find the equivalent
> functionality in mkfs.ext[24],
> and I think its simply not there.
> I think only genext2fs allows
> to set the perms for the root
> node, so I am not sure why it
> is borderline.

 Because on the target, you can generally simply do an mkfs directly of the
partition, then mount it, then set permissions and/or populate it as needed.
Even the mkfs from busybox is usually sufficient on target.

 Of course, Buildroot lives for borderline cases :-). But specifically for the
stable branches, I don't think it's very cost-effective to spend time on making
them work, unless there's a user who does the effort (because they actually need
it).

 Regards,
 Arnout

> Are there any alternatives?
> Buildroot currently creates
> only the rootfs image, and
> for that the default root node
> perms are fine. But once you
> want to create the user partition,
> I think genext2fs is the only
> solution.
> Please let me know if this is
> wrong.
Peter Korsgaard April 19, 2021, 7:23 p.m. UTC | #4
>>>>> "Arnout" == Arnout Vandecappelle <arnout@mind.be> writes:

Hi,

 >>>   I guess for stable, the patch [1] should be backported rather than bumping. But
 >>> is it worth spending effort on that for a borderline package like genext2fs?
 >> I tried really hard (before
 >> reporting the above bug)
 >> to find the equivalent
 >> functionality in mkfs.ext[24],
 >> and I think its simply not there.
 >> I think only genext2fs allows
 >> to set the perms for the root
 >> node, so I am not sure why it
 >> is borderline.

 >  Because on the target, you can generally simply do an mkfs directly of the
 > partition, then mount it, then set permissions and/or populate it as needed.
 > Even the mkfs from busybox is usually sufficient on target.

 >  Of course, Buildroot lives for borderline cases :-). But specifically for the
 > stable branches, I don't think it's very cost-effective to spend time on making
 > them work, unless there's a user who does the effort (because they actually need
 > it).

So in case the above is unclear, could you please do a backport of the
patch (and verify that it works) and send a patch for the LTS branch
(2021.02.x)?
Peter Korsgaard April 25, 2021, 7:44 a.m. UTC | #5
>>>>> "Peter" == Peter Korsgaard <peter@korsgaard.com> writes:

Hi,

 >> Because on the target, you can generally simply do an mkfs directly of the
 >> partition, then mount it, then set permissions and/or populate it as needed.
 >> Even the mkfs from busybox is usually sufficient on target.

 >> Of course, Buildroot lives for borderline cases :-). But specifically for the
 >> stable branches, I don't think it's very cost-effective to spend time on making
 >> them work, unless there's a user who does the effort (because they actually need
 >> it).

 > So in case the above is unclear, could you please do a backport of the
 > patch (and verify that it works) and send a patch for the LTS branch
 > (2021.02.x)?

I ended up doing it myself:

https://git.buildroot.net/buildroot/commit/?h=2021.02.x&id=a4822f31ca985192595cf04246e4d6c1f88f168f

Please verify that it works for you, thanks.
diff mbox series

Patch

diff --git a/package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch b/package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch
deleted file mode 100644
index 755ee9dee2..0000000000
--- a/package/genext2fs/0001-update-genext2fs.c-to-rev-1.118.patch
+++ /dev/null
@@ -1,2971 +0,0 @@ 
-[PATCH] update genext2fs.c to CVS rev 1.118
-
-See http://genext2fs.cvs.sourceforge.net/viewvc/genext2fs/genext2fs/genext2fs.c?view=log
-for details.
-
-Numerous bugfixes, large file and filesystem support, rev 1 filesystems,
-volume id support, block size, ..
-
-Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
----
- cache.h     |  128 ++++
- genext2fs.c | 1870 ++++++++++++++++++++++++++++++++++++++++++------------------
- list.h      |   78 ++
- 3 files changed, 1527 insertions(+), 549 deletions(-)
-
-Index: genext2fs-1.4.1/genext2fs.c
-===================================================================
---- genext2fs-1.4.1.orig/genext2fs.c
-+++ genext2fs-1.4.1/genext2fs.c
-@@ -53,6 +53,12 @@
- // 			along with -q, -P, -U
- 
- 
-+/*
-+ * Allow fseeko/off_t to be 64-bit offsets to allow filesystems and
-+ * individual files >2GB.
-+ */
-+#define _FILE_OFFSET_BITS 64
-+
- #include <config.h>
- #include <stdio.h>
- 
-@@ -107,10 +113,8 @@
- 
- #if HAVE_DIRENT_H
- # include <dirent.h>
--# define NAMLEN(dirent) strlen((dirent)->d_name)
- #else
- # define dirent direct
--# define NAMLEN(dirent) (dirent)->d_namlen
- # if HAVE_SYS_NDIR_H
- #  include <sys/ndir.h>
- # endif
-@@ -144,6 +148,8 @@
- # include <limits.h>
- #endif
- 
-+#include "cache.h"
-+
- struct stats {
- 	unsigned long nblocks;
- 	unsigned long ninodes;
-@@ -151,13 +157,42 @@
- 
- // block size
- 
--#define BLOCKSIZE         1024
-+static int blocksize = 1024;
-+
-+#define SUPERBLOCK_OFFSET	1024
-+#define SUPERBLOCK_SIZE		1024
-+
-+#define BLOCKSIZE         blocksize
- #define BLOCKS_PER_GROUP  8192
- #define INODES_PER_GROUP  8192
- /* Percentage of blocks that are reserved.*/
- #define RESERVED_BLOCKS       5/100
- #define MAX_RESERVED_BLOCKS  25/100
- 
-+/* The default value for s_creator_os. */
-+#if defined(__linux__)    &&    defined(EXT2_OS_LINUX)
-+#define CREATOR_OS EXT2_OS_LINUX
-+#define CREATOR_OS_NAME "linux"
-+#else
-+#if defined(__GNU__)     &&     defined(EXT2_OS_HURD)
-+#define CREATOR_OS EXT2_OS_HURD
-+#define CREATOR_OS_NAME "hurd"
-+#else
-+#if defined(__FreeBSD__) &&     defined(EXT2_OS_FREEBSD)
-+#define CREATOR_OS EXT2_OS_FREEBSD
-+#define CREATOR_OS_NAME "freebsd"
-+#else
-+#if defined(LITES)         &&   defined(EXT2_OS_LITES)
-+#define CREATOR_OS EXT2_OS_LITES
-+#define CREATOR_OS_NAME "lites"
-+#else
-+#define CREATOR_OS EXT2_OS_LINUX /* by default */
-+#define CREATOR_OS_NAME "linux"
-+#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
-+#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
-+#endif /* defined(__GNU__)     && defined(EXT2_OS_HURD) */
-+#endif /* defined(__linux__)   && defined(EXT2_OS_LINUX) */
-+
- 
- // inode block size (why is it != BLOCKSIZE ?!?)
- /* The field i_blocks in the ext2 inode stores the number of data blocks
-@@ -190,6 +225,14 @@
- #define EXT2_TIND_BLOCK    14                    // triple indirect block
- #define EXT2_INIT_BLOCK    0xFFFFFFFF            // just initialized (not really a block address)
- 
-+// codes for operating systems
-+
-+#define EXT2_OS_LINUX           0
-+#define EXT2_OS_HURD            1
-+#define EXT2_OS_MASIX           2
-+#define EXT2_OS_FREEBSD         3
-+#define EXT2_OS_LITES           4
-+
- // end of a block walk
- 
- #define WALK_END           0xFFFFFFFE
-@@ -227,44 +270,46 @@
- #define FM_IWOTH   0000002	// write
- #define FM_IXOTH   0000001	// execute
- 
--// options
--
--#define OP_HOLES     0x01       // make files with holes
--
- /* Defines for accessing group details */
- 
- // Number of groups in the filesystem
- #define GRP_NBGROUPS(fs) \
--	(((fs)->sb.s_blocks_count - fs->sb.s_first_data_block + \
--	  (fs)->sb.s_blocks_per_group - 1) / (fs)->sb.s_blocks_per_group)
-+	(((fs)->sb->s_blocks_count - fs->sb->s_first_data_block + \
-+	  (fs)->sb->s_blocks_per_group - 1) / (fs)->sb->s_blocks_per_group)
- 
- // Get group block bitmap (bbm) given the group number
--#define GRP_GET_GROUP_BBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_block_bitmap) )
-+#define GRP_GET_GROUP_BBM(fs,grp,bi) (get_blk((fs),(grp)->bg_block_bitmap,(bi)))
-+#define GRP_PUT_GROUP_BBM(bi) ( put_blk((bi)) )
- 
- // Get group inode bitmap (ibm) given the group number
--#define GRP_GET_GROUP_IBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_inode_bitmap) )
--		
-+#define GRP_GET_GROUP_IBM(fs,grp,bi) (get_blk((fs), (grp)->bg_inode_bitmap,(bi)))
-+#define GRP_PUT_GROUP_IBM(bi) ( put_blk((bi)) )
-+
- // Given an inode number find the group it belongs to
--#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb.s_inodes_per_group)
-+#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb->s_inodes_per_group)
- 
- //Given an inode number get the inode bitmap that covers it
--#define GRP_GET_INODE_BITMAP(fs,nod) \
--	( GRP_GET_GROUP_IBM((fs),GRP_GROUP_OF_INODE((fs),(nod))) )
-+#define GRP_GET_INODE_BITMAP(fs,nod,bi,gi)				\
-+	( GRP_GET_GROUP_IBM((fs),get_gd(fs,GRP_GROUP_OF_INODE((fs),(nod)),gi),bi) )
-+#define GRP_PUT_INODE_BITMAP(bi,gi)		\
-+	( GRP_PUT_GROUP_IBM((bi)),put_gd((gi)) )
- 
- //Given an inode number find its offset within the inode bitmap that covers it
- #define GRP_IBM_OFFSET(fs,nod) \
--	( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb.s_inodes_per_group )
-+	( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb->s_inodes_per_group )
- 
- // Given a block number find the group it belongs to
--#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb.s_blocks_per_group)
-+#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb->s_blocks_per_group)
- 	
--//Given a block number get the block bitmap that covers it
--#define GRP_GET_BLOCK_BITMAP(fs,blk) \
--	( GRP_GET_GROUP_BBM((fs),GRP_GROUP_OF_BLOCK((fs),(blk))) )
-+//Given a block number get/put the block bitmap that covers it
-+#define GRP_GET_BLOCK_BITMAP(fs,blk,bi,gi)				\
-+	( GRP_GET_GROUP_BBM((fs),get_gd(fs,GRP_GROUP_OF_BLOCK((fs),(blk)),(gi)),(bi)) )
-+#define GRP_PUT_BLOCK_BITMAP(bi,gi)		\
-+	( GRP_PUT_GROUP_BBM((bi)),put_gd((gi)) )
- 
- //Given a block number find its offset within the block bitmap that covers it
- #define GRP_BBM_OFFSET(fs,blk) \
--	( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb.s_blocks_per_group )
-+	( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb->s_blocks_per_group )
- 
- 
- // used types
-@@ -286,7 +331,9 @@
- // older solaris. Note that this is still not very portable, in that
- // the return value cannot be trusted.
- 
--#if SCANF_CAN_MALLOC
-+#if 0 // SCANF_CAN_MALLOC
-+// C99 define "a" for floating point, so you can have runtime surprise
-+// according the library versions
- # define SCANF_PREFIX "a"
- # define SCANF_STRING(s) (&s)
- #else
-@@ -430,6 +477,17 @@
- 			((val<<8)&0xFF0000) | (val<<24));
- }
- 
-+static inline int
-+is_blk_empty(uint8 *b)
-+{
-+	uint32 i;
-+	uint32 *v = (uint32 *) b;
-+
-+	for(i = 0; i < BLOCKSIZE / 4; i++)
-+		if (*v++)
-+			return 0;
-+	return 1;
-+}
- 
- // on-disk structures
- // this trick makes me declare things only once
-@@ -460,7 +518,22 @@
- 	udecl32(s_creator_os)          /* Indicator of which OS created the filesystem */ \
- 	udecl32(s_rev_level)           /* The revision level of the filesystem */ \
- 	udecl16(s_def_resuid)          /* The default uid for reserved blocks */ \
--	udecl16(s_def_resgid)          /* The default gid for reserved blocks */
-+	udecl16(s_def_resgid)          /* The default gid for reserved blocks */ \
-+	/* rev 1 version fields start here */ \
-+	udecl32(s_first_ino) 		/* First non-reserved inode */	\
-+	udecl16(s_inode_size) 		/* size of inode structure */	\
-+	udecl16(s_block_group_nr) 	/* block group # of this superblock */ \
-+	udecl32(s_feature_compat) 	/* compatible feature set */	\
-+	udecl32(s_feature_incompat) 	/* incompatible feature set */	\
-+	udecl32(s_feature_ro_compat) 	/* readonly-compatible feature set */ \
-+	utdecl8(s_uuid,16)		/* 128-bit uuid for volume */	\
-+	utdecl8(s_volume_name,16) 	/* volume name */		\
-+	utdecl8(s_last_mounted,64) 	/* directory where last mounted */ \
-+	udecl32(s_algorithm_usage_bitmap) /* For compression */
-+
-+#define EXT2_GOOD_OLD_FIRST_INO	11
-+#define EXT2_GOOD_OLD_INODE_SIZE 128
-+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
- 
- #define groupdescriptor_decl \
- 	udecl32(bg_block_bitmap)       /* Block number of the block bitmap */ \
-@@ -500,6 +573,7 @@
- 
- #define decl8(x) int8 x;
- #define udecl8(x) uint8 x;
-+#define utdecl8(x,n) uint8 x[n];
- #define decl16(x) int16 x;
- #define udecl16(x) uint16 x;
- #define decl32(x) int32 x;
-@@ -509,7 +583,7 @@
- typedef struct
- {
- 	superblock_decl
--	uint32 s_reserved[235];       // Reserved
-+	uint32 s_reserved[205];       // Reserved
- } superblock;
- 
- typedef struct
-@@ -527,10 +601,9 @@
- typedef struct
- {
- 	directory_decl
--	char d_name[0];
- } directory;
- 
--typedef uint8 block[BLOCKSIZE];
-+typedef uint8 *block;
- 
- /* blockwalker fields:
-    The blockwalker is used to access all the blocks of a file (including
-@@ -567,23 +640,41 @@
- 	uint32 bptind;
- } blockwalker;
- 
-+#define HDLINK_CNT   16
-+struct hdlink_s
-+{
-+	uint32	src_inode;
-+	uint32	dst_nod;
-+};
-+
-+struct hdlinks_s
-+{
-+	int32 count;
-+	struct hdlink_s *hdl;
-+};
- 
- /* Filesystem structure that support groups */
--#if BLOCKSIZE == 1024
- typedef struct
- {
--	block zero;            // The famous block 0
--	superblock sb;         // The superblock
--	groupdescriptor gd[0]; // The group descriptors
-+	FILE *f;
-+	superblock *sb;
-+	int swapit;
-+	int32 hdlink_cnt;
-+	struct hdlinks_s hdlinks;
-+
-+	int holes;
-+
-+	listcache blks;
-+	listcache gds;
-+	listcache inodes;
-+	listcache blkmaps;
- } filesystem;
--#else
--#error UNHANDLED BLOCKSIZE
--#endif
- 
- // now the endianness swap
- 
- #undef decl8
- #undef udecl8
-+#undef utdecl8
- #undef decl16
- #undef udecl16
- #undef decl32
-@@ -592,28 +683,13 @@
- 
- #define decl8(x)
- #define udecl8(x)
-+#define utdecl8(x,n)
- #define decl16(x) this->x = swab16(this->x);
- #define udecl16(x) this->x = swab16(this->x);
- #define decl32(x) this->x = swab32(this->x);
- #define udecl32(x) this->x = swab32(this->x);
- #define utdecl32(x,n) { int i; for(i=0; i<n; i++) this->x[i] = swab32(this->x[i]); }
- 
--#define HDLINK_CNT   16
--static int32 hdlink_cnt = HDLINK_CNT;
--struct hdlink_s
--{
--	uint32	src_inode;
--	uint32	dst_nod;
--};
--
--struct hdlinks_s 
--{
--	int32 count;
--	struct hdlink_s *hdl;
--};
--
--static struct hdlinks_s hdlinks;
--
- static void
- swap_sb(superblock *sb)
- {
-@@ -633,9 +709,24 @@
- static void
- swap_nod(inode *nod)
- {
-+	uint32 nblk;
-+
- #define this nod
- 	inode_decl
- #undef this
-+
-+	// block and character inodes store the major and minor in the
-+	// i_block, so we need to unswap to get those.  Also, if it's
-+	// zero iblocks, put the data back like it belongs.
-+	nblk = nod->i_blocks / INOBLK;
-+	if ((nod->i_size && !nblk)
-+	    || ((nod->i_mode & FM_IFBLK) == FM_IFBLK)
-+	    || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
-+	{
-+		int i;
-+		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
-+			nod->i_block[i] = swab32(nod->i_block[i]);
-+	}
- }
- 
- static void
-@@ -657,6 +748,7 @@
- 
- #undef decl8
- #undef udecl8
-+#undef utdecl8
- #undef decl16
- #undef udecl16
- #undef decl32
-@@ -770,15 +862,15 @@
- }
- 
- int
--is_hardlink(ino_t inode)
-+is_hardlink(filesystem *fs, ino_t inode)
- {
- 	int i;
- 
--	for(i = 0; i < hdlinks.count; i++) {
--		if(hdlinks.hdl[i].src_inode == inode)
-+	for(i = 0; i < fs->hdlinks.count; i++) {
-+		if(fs->hdlinks.hdl[i].src_inode == inode)
- 			return i;
- 	}
--	return -1;		
-+	return -1;
- }
- 
- // printf helper macro
-@@ -789,6 +881,8 @@
- get_workblk(void)
- {
- 	unsigned char* b=calloc(1,BLOCKSIZE);
-+	if (!b)
-+		error_msg_and_die("get_workblk() failed, out of memory");
- 	return b;
- }
- static inline void
-@@ -811,24 +905,464 @@
- 	return b[(item-1) / 8] & (1 << ((item-1) % 8));
- }
- 
--// return a given block from a filesystem
-+// Used by get_blk/put_blk to hold information about a block owned
-+// by the user.
-+typedef struct
-+{
-+	cache_link link;
-+
-+	filesystem *fs;
-+	uint32 blk;
-+	uint8 *b;
-+	uint32 usecount;
-+} blk_info;
-+
-+#define MAX_FREE_CACHE_BLOCKS 100
-+
-+static uint32
-+blk_elem_val(cache_link *elem)
-+{
-+	blk_info *bi = container_of(elem, blk_info, link);
-+	return bi->blk;
-+}
-+
-+static void
-+blk_freed(cache_link *elem)
-+{
-+	blk_info *bi = container_of(elem, blk_info, link);
-+
-+	if (fseeko(bi->fs->f, ((off_t) bi->blk) * BLOCKSIZE, SEEK_SET))
-+		perror_msg_and_die("fseek");
-+	if (fwrite(bi->b, BLOCKSIZE, 1, bi->fs->f) != 1)
-+		perror_msg_and_die("get_blk: write");
-+	free(bi->b);
-+	free(bi);
-+}
-+
-+// Return a given block from a filesystem.  Make sure to call
-+// put_blk when you are done with it.
- static inline uint8 *
--get_blk(filesystem *fs, uint32 blk)
-+get_blk(filesystem *fs, uint32 blk, blk_info **rbi)
- {
--	return (uint8*)fs + blk*BLOCKSIZE;
-+	cache_link *curr;
-+	blk_info *bi;
-+
-+	if (blk >= fs->sb->s_blocks_count)
-+		error_msg_and_die("Internal error, block out of range");
-+
-+	curr = cache_find(&fs->blks, blk);
-+	if (curr) {
-+		bi = container_of(curr, blk_info, link);
-+		bi->usecount++;
-+		goto out;
-+	}
-+
-+	bi = malloc(sizeof(*bi));
-+	if (!bi)
-+		error_msg_and_die("get_blk: out of memory");
-+	bi->fs = fs;
-+	bi->blk = blk;
-+	bi->usecount = 1;
-+	bi->b = malloc(BLOCKSIZE);
-+	if (!bi->b)
-+		error_msg_and_die("get_blk: out of memory");
-+	cache_add(&fs->blks, &bi->link);
-+	if (fseeko(fs->f, ((off_t) blk) * BLOCKSIZE, SEEK_SET))
-+		perror_msg_and_die("fseek");
-+	if (fread(bi->b, BLOCKSIZE, 1, fs->f) != 1) {
-+		if (ferror(fs->f))
-+			perror_msg_and_die("fread");
-+		memset(bi->b, 0, BLOCKSIZE);
-+	}
-+
-+out:
-+	*rbi = bi;
-+	return bi->b;
- }
- 
- // return a given inode from a filesystem
--static inline inode *
--get_nod(filesystem *fs, uint32 nod)
-+static inline void
-+put_blk(blk_info *bi)
-+{
-+	if (bi->usecount == 0)
-+		error_msg_and_die("Internal error: put_blk usecount zero");
-+	bi->usecount--;
-+	if (bi->usecount == 0)
-+		/* Free happens in the cache code */
-+		cache_item_set_unused(&bi->fs->blks, &bi->link);
-+}
-+
-+typedef struct
- {
--	int grp,offset;
-+	cache_link link;
-+
-+	filesystem *fs;
-+	int gds;
-+	blk_info *bi;
-+	groupdescriptor *gd;
-+	uint32 usecount;
-+} gd_info;
-+
-+#define MAX_FREE_CACHE_GDS 100
-+
-+static uint32
-+gd_elem_val(cache_link *elem)
-+{
-+	gd_info *gi = container_of(elem, gd_info, link);
-+	return gi->gds;
-+}
-+
-+static void
-+gd_freed(cache_link *elem)
-+{
-+	gd_info *gi = container_of(elem, gd_info, link);
-+
-+	if (gi->fs->swapit)
-+		swap_gd(gi->gd);
-+	put_blk(gi->bi);
-+	free(gi);
-+}
-+
-+#define GDS_START ((SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE + BLOCKSIZE - 1) / BLOCKSIZE)
-+#define GDS_PER_BLOCK (BLOCKSIZE / sizeof(groupdescriptor))
-+// the group descriptors are aligned on the block size
-+static inline groupdescriptor *
-+get_gd(filesystem *fs, uint32 no, gd_info **rgi)
-+{
-+	uint32 gdblk;
-+	uint32 offset;
-+	gd_info *gi;
-+	cache_link *curr;
-+
-+	curr = cache_find(&fs->gds, no);
-+	if (curr) {
-+		gi = container_of(curr, gd_info, link);
-+		gi->usecount++;
-+		goto out;
-+	}
-+
-+	gi = malloc(sizeof(*gi));
-+	if (!gi)
-+		error_msg_and_die("get_gd: out of memory");
-+	gi->fs = fs;
-+	gi->gds = no;
-+	gi->usecount = 1;
-+	gdblk = GDS_START + (no / GDS_PER_BLOCK);
-+	offset = no % GDS_PER_BLOCK;
-+	gi->gd = ((groupdescriptor *) get_blk(fs, gdblk, &gi->bi)) + offset;
-+	cache_add(&fs->gds, &gi->link);
-+	if (fs->swapit)
-+		swap_gd(gi->gd);
-+ out:
-+	*rgi = gi;
-+
-+	return gi->gd;
-+}
-+
-+static inline void
-+put_gd(gd_info *gi)
-+{
-+	if (gi->usecount == 0)
-+		error_msg_and_die("Internal error: put_gd usecount zero");
-+
-+	gi->usecount--;
-+	if (gi->usecount == 0)
-+		/* Free happens in the cache code */
-+		cache_item_set_unused(&gi->fs->gds, &gi->link);
-+}
-+
-+// Used by get_blkmap/put_blkmap to hold information about an block map
-+// owned by the user.
-+typedef struct
-+{
-+	cache_link link;
-+
-+	filesystem *fs;
-+	uint32 blk;
-+	uint8 *b;
-+	blk_info *bi;
-+	uint32 usecount;
-+} blkmap_info;
-+
-+#define MAX_FREE_CACHE_BLOCKMAPS 100
-+
-+static uint32
-+blkmap_elem_val(cache_link *elem)
-+{
-+	blkmap_info *bmi = container_of(elem, blkmap_info, link);
-+	return bmi->blk;
-+}
-+
-+static void
-+blkmap_freed(cache_link *elem)
-+{
-+	blkmap_info *bmi = container_of(elem, blkmap_info, link);
-+
-+	if (bmi->fs->swapit)
-+		swap_block(bmi->b);
-+	put_blk(bmi->bi);
-+	free(bmi);
-+}
-+
-+// Return a given block map from a filesystem.  Make sure to call
-+// put_blkmap when you are done with it.
-+static inline uint32 *
-+get_blkmap(filesystem *fs, uint32 blk, blkmap_info **rbmi)
-+{
-+	blkmap_info *bmi;
-+	cache_link *curr;
-+
-+	curr = cache_find(&fs->blkmaps, blk);
-+	if (curr) {
-+		bmi = container_of(curr, blkmap_info, link);
-+		bmi->usecount++;
-+		goto out;
-+	}
-+
-+	bmi = malloc(sizeof(*bmi));
-+	if (!bmi)
-+		error_msg_and_die("get_blkmap: out of memory");
-+	bmi->fs = fs;
-+	bmi->blk = blk;
-+	bmi->b = get_blk(fs, blk, &bmi->bi);
-+	bmi->usecount = 1;
-+	cache_add(&fs->blkmaps, &bmi->link);
-+
-+	if (fs->swapit)
-+		swap_block(bmi->b);
-+ out:
-+	*rbmi = bmi;
-+	return (uint32 *) bmi->b;
-+}
-+
-+static inline void
-+put_blkmap(blkmap_info *bmi)
-+{
-+	if (bmi->usecount == 0)
-+		error_msg_and_die("Internal error: put_blkmap usecount zero");
-+
-+	bmi->usecount--;
-+	if (bmi->usecount == 0)
-+		/* Free happens in the cache code */
-+		cache_item_set_unused(&bmi->fs->blkmaps, &bmi->link);
-+}
-+
-+// Used by get_nod/put_nod to hold information about an inode owned
-+// by the user.
-+typedef struct
-+{
-+	cache_link link;
-+
-+	filesystem *fs;
-+	uint32 nod;
-+	uint8 *b;
-+	blk_info *bi;
- 	inode *itab;
-+	uint32 usecount;
-+} nod_info;
-+
-+#define MAX_FREE_CACHE_INODES 100
-+
-+static uint32
-+inode_elem_val(cache_link *elem)
-+{
-+	nod_info *ni = container_of(elem, nod_info, link);
-+	return ni->nod;
-+}
-+
-+static void
-+inode_freed(cache_link *elem)
-+{
-+	nod_info *ni = container_of(elem, nod_info, link);
-+
-+	if (ni->fs->swapit)
-+		swap_nod(ni->itab);
-+	put_blk(ni->bi);
-+	free(ni);
-+}
-+
-+#define INODES_PER_BLOCK (BLOCKSIZE / sizeof(inode))
- 
--	offset = GRP_IBM_OFFSET(fs,nod);
-+// return a given inode from a filesystem
-+static inline inode *
-+get_nod(filesystem *fs, uint32 nod, nod_info **rni)
-+{
-+	uint32 grp, boffset, offset;
-+	cache_link *curr;
-+	groupdescriptor *gd;
-+	gd_info *gi;
-+	nod_info *ni;
-+
-+	curr = cache_find(&fs->inodes, nod);
-+	if (curr) {
-+		ni = container_of(curr, nod_info, link);
-+		ni->usecount++;
-+		goto out;
-+	}
-+
-+	ni = malloc(sizeof(*ni));
-+	if (!ni)
-+		error_msg_and_die("get_nod: out of memory");
-+	ni->fs = fs;
-+	ni->nod = nod;
-+	ni->usecount = 1;
-+	cache_add(&fs->inodes, &ni->link);
-+
-+	offset = GRP_IBM_OFFSET(fs,nod) - 1;
-+	boffset = offset / INODES_PER_BLOCK;
-+	offset %= INODES_PER_BLOCK;
- 	grp = GRP_GROUP_OF_INODE(fs,nod);
--	itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table);
--	return itab+offset-1;
-+	gd = get_gd(fs, grp, &gi);
-+	ni->b = get_blk(fs, gd->bg_inode_table + boffset, &ni->bi);
-+	ni->itab = ((inode *) ni->b) + offset;
-+	if (fs->swapit)
-+		swap_nod(ni->itab);
-+	put_gd(gi);
-+ out:
-+	*rni = ni;
-+	return ni->itab;
-+}
-+
-+static inline void
-+put_nod(nod_info *ni)
-+{
-+	if (ni->usecount == 0)
-+		error_msg_and_die("Internal error: put_nod usecount zero");
-+
-+	ni->usecount--;
-+	if (ni->usecount == 0)
-+		/* Free happens in the cache code */
-+		cache_item_set_unused(&ni->fs->inodes, &ni->link);
-+}
-+
-+// Used to hold state information while walking a directory inode.
-+typedef struct
-+{
-+	directory d;
-+	filesystem *fs;
-+	uint32 nod;
-+	directory *last_d;
-+	uint8 *b;
-+	blk_info *bi;
-+} dirwalker;
-+
-+// Start a directory walk on the given inode.  You must pass in a
-+// dirwalker structure, then use that dirwalker for future operations.
-+// Call put_dir when you are done walking the directory.
-+static inline directory *
-+get_dir(filesystem *fs, uint32 nod, dirwalker *dw)
-+{
-+	dw->fs = fs;
-+	dw->b = get_blk(fs, nod, &dw->bi);
-+	dw->nod = nod;
-+	dw->last_d = (directory *) dw->b;
-+
-+	memcpy(&dw->d, dw->last_d, sizeof(directory));
-+	if (fs->swapit)
-+		swap_dir(&dw->d);
-+	return &dw->d;
-+}
-+
-+// Move to the next directory.
-+static inline directory *
-+next_dir(dirwalker *dw)
-+{
-+	directory *next_d = (directory *)((int8*)dw->last_d + dw->d.d_rec_len);
-+
-+	if (dw->fs->swapit)
-+		swap_dir(&dw->d);
-+	memcpy(dw->last_d, &dw->d, sizeof(directory));
-+
-+	if (((int8 *) next_d) >= ((int8 *) dw->b + BLOCKSIZE))
-+		return NULL;
-+
-+	dw->last_d = next_d;
-+	memcpy(&dw->d, next_d, sizeof(directory));
-+	if (dw->fs->swapit)
-+		swap_dir(&dw->d);
-+	return &dw->d;
-+}
-+
-+// Call then when you are done with the directory walk.
-+static inline void
-+put_dir(dirwalker *dw)
-+{
-+	if (dw->fs->swapit)
-+		swap_dir(&dw->d);
-+	memcpy(dw->last_d, &dw->d, sizeof(directory));
-+
-+	if (dw->nod == 0)
-+		free_workblk(dw->b);
-+	else
-+		put_blk(dw->bi);
-+}
-+
-+// Create a new directory block with the given inode as it's destination
-+// and append it to the current dirwalker.
-+static directory *
-+new_dir(filesystem *fs, uint32 dnod, const char *name, int nlen, dirwalker *dw)
-+{
-+	directory *d;
-+
-+	dw->fs = fs;
-+	dw->b = get_workblk();
-+	dw->nod = 0;
-+	dw->last_d = (directory *) dw->b;
-+	d = &dw->d;
-+	d->d_inode = dnod;
-+	d->d_rec_len = BLOCKSIZE;
-+	d->d_name_len = nlen;
-+	strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
-+	return d;
-+}
-+
-+// Shrink the current directory entry, make a new one with the free
-+// space, and return the new directory entry (making it current).
-+static inline directory *
-+shrink_dir(dirwalker *dw, uint32 nod, const char *name, int nlen)
-+{
-+	int reclen, preclen;
-+	directory *d = &dw->d;
-+
-+	reclen = d->d_rec_len;
-+	d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
-+	preclen = d->d_rec_len;
-+	reclen -= preclen;
-+	if (dw->fs->swapit)
-+		swap_dir(&dw->d);
-+	memcpy(dw->last_d, &dw->d, sizeof(directory));
-+
-+	dw->last_d = (directory *) (((int8 *) dw->last_d) + preclen);
-+	d->d_rec_len = reclen;
-+	d->d_inode = nod;
-+	d->d_name_len = nlen;
-+	strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
-+
-+	return d;
-+}
-+
-+// Return the current block the directory is walking
-+static inline uint8 *
-+dir_data(dirwalker *dw)
-+{
-+	return dw->b;
-+}
-+
-+// Return the pointer to the name for the current directory
-+static inline char *
-+dir_name(dirwalker *dw)
-+{
-+	return ((char *) dw->last_d) + sizeof(directory);
-+}
-+
-+// Set the name for the current directory.  Note that this doesn't
-+// verify that there is space for the directory name, you must do
-+// that yourself.
-+static void
-+dir_set_name(dirwalker *dw, const char *name, int nlen)
-+{
-+	dw->d.d_name_len = nlen;
-+	strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
- }
- 
- // allocate a given block/inode in the bitmap
-@@ -870,21 +1404,34 @@
- {
- 	uint32 bk=0;
- 	uint32 grp,nbgroups;
-+	blk_info *bi;
-+	groupdescriptor *gd;
-+	gd_info *gi;
- 
- 	grp = GRP_GROUP_OF_INODE(fs,nod);
- 	nbgroups = GRP_NBGROUPS(fs);
--	if(!(bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0))) {
--		for(grp=0;grp<nbgroups && !bk;grp++)
--			bk=allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap),0);
-+	gd = get_gd(fs, grp, &gi);
-+	bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0);
-+	GRP_PUT_GROUP_BBM(bi);
-+	put_gd(gi);
-+	if (!bk) {
-+		for (grp=0; grp<nbgroups && !bk; grp++) {
-+			gd = get_gd(fs, grp, &gi);
-+			bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0);
-+			GRP_PUT_GROUP_BBM(bi);
-+			put_gd(gi);
-+		}
- 		grp--;
- 	}
- 	if (!bk)
- 		error_msg_and_die("couldn't allocate a block (no free space)");
--	if(!(fs->gd[grp].bg_free_blocks_count--))
-+	gd = get_gd(fs, grp, &gi);
-+	if(!(gd->bg_free_blocks_count--))
- 		error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)",grp);
--	if(!(fs->sb.s_free_blocks_count--))
-+	put_gd(gi);
-+	if(!(fs->sb->s_free_blocks_count--))
- 		error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
--	return fs->sb.s_blocks_per_group*grp + bk;
-+	return fs->sb->s_first_data_block + fs->sb->s_blocks_per_group*grp + (bk-1);
- }
- 
- // free a block
-@@ -892,12 +1439,18 @@
- free_blk(filesystem *fs, uint32 bk)
- {
- 	uint32 grp;
--
--	grp = bk / fs->sb.s_blocks_per_group;
--	bk %= fs->sb.s_blocks_per_group;
--	deallocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), bk);
--	fs->gd[grp].bg_free_blocks_count++;
--	fs->sb.s_free_blocks_count++;
-+	blk_info *bi;
-+	gd_info *gi;
-+	groupdescriptor *gd;
-+
-+	grp = bk / fs->sb->s_blocks_per_group;
-+	bk %= fs->sb->s_blocks_per_group;
-+	gd = get_gd(fs, grp, &gi);
-+	deallocate(GRP_GET_GROUP_BBM(fs, gd, &bi), bk);
-+	GRP_PUT_GROUP_BBM(bi);
-+	gd->bg_free_blocks_count++;
-+	put_gd(gi);
-+	fs->sb->s_free_blocks_count++;
- }
- 
- // allocate an inode
-@@ -906,6 +1459,9 @@
- {
- 	uint32 nod,best_group=0;
- 	uint32 grp,nbgroups,avefreei;
-+	blk_info *bi;
-+	gd_info *gi, *bestgi;
-+	groupdescriptor *gd, *bestgd;
- 
- 	nbgroups = GRP_NBGROUPS(fs);
- 
-@@ -914,22 +1470,32 @@
- 	/* find the one with the most free blocks and allocate node there     */
- 	/* Idea from find_group_dir in fs/ext2/ialloc.c in 2.4.19 kernel      */
- 	/* We do it for all inodes.                                           */
--	avefreei  =  fs->sb.s_free_inodes_count / nbgroups;
-+	avefreei  =  fs->sb->s_free_inodes_count / nbgroups;
-+	bestgd = get_gd(fs, best_group, &bestgi);
- 	for(grp=0; grp<nbgroups; grp++) {
--		if (fs->gd[grp].bg_free_inodes_count < avefreei ||
--		    fs->gd[grp].bg_free_inodes_count == 0)
-+		gd = get_gd(fs, grp, &gi);
-+		if (gd->bg_free_inodes_count < avefreei ||
-+		    gd->bg_free_inodes_count == 0) {
-+			put_gd(gi);
- 			continue;
--		if (!best_group || 
--			fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count)
-+		}
-+		if (!best_group || gd->bg_free_blocks_count > bestgd->bg_free_blocks_count) {
-+			put_gd(bestgi);
- 			best_group = grp;
-+			bestgd = gd;
-+			bestgi = gi;
-+		} else
-+			put_gd(gi);
- 	}
--	if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0)))
-+	if (!(nod = allocate(GRP_GET_GROUP_IBM(fs, bestgd, &bi), 0)))
- 		error_msg_and_die("couldn't allocate an inode (no free inode)");
--	if(!(fs->gd[best_group].bg_free_inodes_count--))
-+	GRP_PUT_GROUP_IBM(bi);
-+	if(!(bestgd->bg_free_inodes_count--))
- 		error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
--	if(!(fs->sb.s_free_inodes_count--))
-+	put_gd(bestgi);
-+	if(!(fs->sb->s_free_inodes_count--))
- 		error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
--	return fs->sb.s_inodes_per_group*best_group+nod;
-+	return fs->sb->s_inodes_per_group*best_group+nod;
- }
- 
- // print a bitmap allocation
-@@ -962,30 +1528,40 @@
- //				  used after being freed, so once you start
- //				  freeing blocks don't stop until the end of
- //				  the file. moreover, i_blocks isn't updated.
--//				  in fact, don't do that, just use extend_blk
- // if hole!=0, create a hole in the file
- static uint32
- walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, int32 *create, uint32 hole)
- {
- 	uint32 *bkref = 0;
-+	uint32 bk = 0;
-+	blkmap_info *bmi1 = NULL, *bmi2 = NULL, *bmi3 = NULL;
- 	uint32 *b;
- 	int extend = 0, reduce = 0;
-+	inode *inod;
-+	nod_info *ni;
-+	uint32 *iblk;
-+
- 	if(create && (*create) < 0)
- 		reduce = 1;
--	if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK)
-+	inod = get_nod(fs, nod, &ni);
-+	if(bw->bnum >= inod->i_blocks / INOBLK)
- 	{
- 		if(create && (*create) > 0)
- 		{
- 			(*create)--;
- 			extend = 1;
- 		}
--		else	
-+		else
-+		{
-+			put_nod(ni);
- 			return WALK_END;
-+		}
- 	}
-+	iblk = inod->i_block;
- 	// first direct block
- 	if(bw->bpdir == EXT2_INIT_BLOCK)
- 	{
--		bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0];
-+		bkref = &iblk[bw->bpdir = 0];
- 		if(extend) // allocate first block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
- 		if(reduce) // free first block
-@@ -994,7 +1570,7 @@
- 	// direct block
- 	else if(bw->bpdir < EXT2_NDIR_BLOCKS)
- 	{
--		bkref = &get_nod(fs, nod)->i_block[++bw->bpdir];
-+		bkref = &iblk[++bw->bpdir];
- 		if(extend) // allocate block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
- 		if(reduce) // free block
-@@ -1007,10 +1583,10 @@
- 		bw->bpdir = EXT2_IND_BLOCK;
- 		bw->bpind = 0;
- 		if(extend) // allocate indirect block
--			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
-+			iblk[bw->bpdir] = alloc_blk(fs,nod);
- 		if(reduce) // free indirect block
--			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
-+			free_blk(fs, iblk[bw->bpdir]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
- 		bkref = &b[bw->bpind];
- 		if(extend) // allocate first block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1021,7 +1597,7 @@
- 	else if((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
- 	{
- 		bw->bpind++;
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
- 		bkref = &b[bw->bpind];
- 		if(extend) // allocate block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1036,15 +1612,15 @@
- 		bw->bpind = 0;
- 		bw->bpdind = 0;
- 		if(extend) // allocate double indirect block
--			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
-+			iblk[bw->bpdir] = alloc_blk(fs,nod);
- 		if(reduce) // free double indirect block
--			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
-+			free_blk(fs, iblk[bw->bpdir]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
- 		if(extend) // allocate first indirect block
- 			b[bw->bpind] = alloc_blk(fs,nod);
- 		if(reduce) // free  firstindirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
- 		bkref = &b[bw->bpdind];
- 		if(extend) // allocate first block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1055,8 +1631,8 @@
- 	else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1))
- 	{
- 		bw->bpdind++;
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
- 		bkref = &b[bw->bpdind];
- 		if(extend) // allocate block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1069,12 +1645,12 @@
- 		bw->bnum++;
- 		bw->bpdind = 0;
- 		bw->bpind++;
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
- 		if(extend) // allocate indirect block
- 			b[bw->bpind] = alloc_blk(fs,nod);
- 		if(reduce) // free indirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
- 		bkref = &b[bw->bpdind];
- 		if(extend) // allocate first block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1094,20 +1670,20 @@
- 		bw->bpdind = 0;
- 		bw->bptind = 0;
- 		if(extend) // allocate triple indirect block
--			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
-+			iblk[bw->bpdir] = alloc_blk(fs,nod);
- 		if(reduce) // free triple indirect block
--			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
-+			free_blk(fs, iblk[bw->bpdir]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
- 		if(extend) // allocate first double indirect block
- 			b[bw->bpind] = alloc_blk(fs,nod);
- 		if(reduce) // free first double indirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
- 		if(extend) // allocate first indirect block
- 			b[bw->bpdind] = alloc_blk(fs,nod);
- 		if(reduce) // free first indirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpdind]);
-+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
- 		bkref = &b[bw->bptind];
- 		if(extend) // allocate first data block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1121,9 +1697,9 @@
- 		  (bw->bptind < BLOCKSIZE/4 -1) )
- 	{
- 		bw->bptind++;
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpdind]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
-+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
- 		bkref = &b[bw->bptind];
- 		if(extend) // allocate data block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1140,13 +1716,13 @@
- 		bw->bnum++;
- 		bw->bptind = 0;
- 		bw->bpdind++;
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
- 		if(extend) // allocate single indirect block
- 			b[bw->bpdind] = alloc_blk(fs,nod);
- 		if(reduce) // free indirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpdind]);
-+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
- 		bkref = &b[bw->bptind];
- 		if(extend) // allocate first data block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1163,17 +1739,17 @@
- 		bw->bpdind = 0;
- 		bw->bptind = 0;
- 		bw->bpind++;
--		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
-+		b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
- 		if(extend) // allocate double indirect block
- 			b[bw->bpind] = alloc_blk(fs,nod);
- 		if(reduce) // free double indirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpind]);
-+		b = get_blkmap(fs, b[bw->bpind], &bmi2);
- 		if(extend) // allocate single indirect block
- 			b[bw->bpdind] = alloc_blk(fs,nod);
- 		if(reduce) // free indirect block
- 			free_blk(fs, b[bw->bpind]);
--		b = (uint32*)get_blk(fs, b[bw->bpdind]);
-+		b = get_blkmap(fs, b[bw->bpdind], &bmi3);
- 		bkref = &b[bw->bptind];
- 		if(extend) // allocate first block
- 			*bkref = hole ? 0 : alloc_blk(fs,nod);
-@@ -1184,56 +1760,105 @@
- 		error_msg_and_die("file too big !"); 
- 	/* End change for walking triple indirection */
- 
--	if(*bkref)
--	{
-+	bk = *bkref;
-+	if (bmi3)
-+		put_blkmap(bmi3);
-+	if (bmi2)
-+		put_blkmap(bmi2);
-+	if (bmi1)
-+		put_blkmap(bmi1);
-+
-+	if(bk)
-+	{
-+		blk_info *bi;
-+		gd_info *gi;
-+		uint8 *block;
- 		bw->bnum++;
--		if(!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref)))
--			error_msg_and_die("[block %d of inode %d is unallocated !]", *bkref, nod);
-+		block = GRP_GET_BLOCK_BITMAP(fs,bk,&bi,&gi);
-+		if(!reduce && !allocated(block, GRP_BBM_OFFSET(fs,bk)))
-+			error_msg_and_die("[block %d of inode %d is unallocated !]", bk, nod);
-+		GRP_PUT_BLOCK_BITMAP(bi, gi);
- 	}
- 	if(extend)
--		get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
--	return *bkref;
-+		inod->i_blocks = bw->bnum * INOBLK;
-+	put_nod(ni);
-+	return bk;
- }
- 
--// add blocks to an inode (file/dir/etc...)
--static void
--extend_blk(filesystem *fs, uint32 nod, block b, int amount)
-+typedef struct
- {
--	int create = amount;
--	blockwalker bw, lbw;
--	uint32 bk;
--	init_bw(&bw);
--	if(amount < 0)
--	{
--		uint32 i;
--		for(i = 0; i < get_nod(fs, nod)->i_blocks / INOBLK + amount; i++)
--			walk_bw(fs, nod, &bw, 0, 0);
--		while(walk_bw(fs, nod, &bw, &create, 0) != WALK_END)
-+	blockwalker bw;
-+	uint32 nod;
-+	nod_info *ni;
-+	inode *inod;
-+} inode_pos;
-+#define INODE_POS_TRUNCATE 0
-+#define INODE_POS_EXTEND 1
-+
-+// Call this to set up an ipos structure for future use with
-+// extend_inode_blk to append blocks to the given inode.  If
-+// op is INODE_POS_TRUNCATE, the inode is truncated to zero size.
-+// If op is INODE_POS_EXTEND, the position is moved to the end
-+// of the inode's data blocks.
-+// Call inode_pos_finish when done with the inode_pos structure.
-+static void
-+inode_pos_init(filesystem *fs, inode_pos *ipos, uint32 nod, int op,
-+	       blockwalker *endbw)
-+{
-+	blockwalker lbw;
-+
-+	init_bw(&ipos->bw);
-+	ipos->nod = nod;
-+	ipos->inod = get_nod(fs, nod, &ipos->ni);
-+	if (op == INODE_POS_TRUNCATE) {
-+		int32 create = -1;
-+		while(walk_bw(fs, nod, &ipos->bw, &create, 0) != WALK_END)
- 			/*nop*/;
--		get_nod(fs, nod)->i_blocks += amount * INOBLK;
-+		ipos->inod->i_blocks = 0;
- 	}
--	else
-+
-+	if (endbw)
-+		ipos->bw = *endbw;
-+	else {
-+		/* Seek to the end */
-+		init_bw(&ipos->bw);
-+		lbw = ipos->bw;
-+		while(walk_bw(fs, nod, &ipos->bw, 0, 0) != WALK_END)
-+			lbw = ipos->bw;
-+		ipos->bw = lbw;
-+	}
-+}
-+
-+// Clean up the inode_pos structure.
-+static void
-+inode_pos_finish(filesystem *fs, inode_pos *ipos)
-+{
-+	put_nod(ipos->ni);
-+}
-+
-+// add blocks to an inode (file/dir/etc...) at the given position.
-+// This will only work when appending to the end of an inode.
-+static void
-+extend_inode_blk(filesystem *fs, inode_pos *ipos, block b, int amount)
-+{
-+	uint32 bk;
-+	uint32 pos;
-+
-+	if (amount < 0)
-+		error_msg_and_die("extend_inode_blk: Got negative amount");
-+
-+	for (pos = 0; amount; pos += BLOCKSIZE)
- 	{
--		lbw = bw;
--		while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
--			lbw = bw;
--		bw = lbw;
--		while(create)
--		{
--			int i, copyb = 0;
--			if(!(fs->sb.s_reserved[200] & OP_HOLES))
--				copyb = 1;
--			else
--				for(i = 0; i < BLOCKSIZE / 4; i++)
--					if(((int32*)(b + BLOCKSIZE * (amount - create)))[i])
--					{
--						copyb = 1;
--						break;
--					}
--			if((bk = walk_bw(fs, nod, &bw, &create, !copyb)) == WALK_END)
--				break;
--			if(copyb)
--				memcpy(get_blk(fs, bk), b + BLOCKSIZE * (amount - create - 1), BLOCKSIZE);
-+		int hole = (fs->holes && is_blk_empty(b + pos));
-+
-+		bk = walk_bw(fs, ipos->nod, &ipos->bw, &amount, hole);
-+		if (bk == WALK_END)
-+			error_msg_and_die("extend_inode_blk: extend failed");
-+		if (!hole) {
-+			blk_info *bi;
-+			uint8 *block = get_blk(fs, bk, &bi);
-+			memcpy(block, b + pos, BLOCKSIZE);
-+			put_blk(bi);
- 		}
- 	}
- }
-@@ -1242,15 +1867,17 @@
- static void
- add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name)
- {
--	blockwalker bw;
-+	blockwalker bw, lbw;
- 	uint32 bk;
--	uint8 *b;
- 	directory *d;
-+	dirwalker dw;
- 	int reclen, nlen;
- 	inode *node;
- 	inode *pnode;
-+	nod_info *dni, *ni;
-+	inode_pos ipos;
- 
--	pnode = get_nod(fs, dnod);
-+	pnode = get_nod(fs, dnod, &dni);
- 	if((pnode->i_mode & FM_IFMT) != FM_IFDIR)
- 		error_msg_and_die("can't add '%s' to a non-directory", name);
- 	if(!*name)
-@@ -1262,52 +1889,52 @@
- 	if(reclen > BLOCKSIZE)
- 		error_msg_and_die("bad name '%s' (too long)", name);
- 	init_bw(&bw);
-+	lbw = bw;
- 	while((bk = walk_bw(fs, dnod, &bw, 0, 0)) != WALK_END) // for all blocks in dir
- 	{
--		b = get_blk(fs, bk);
- 		// for all dir entries in block
--		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
-+		for(d = get_dir(fs, bk, &dw); d; d = next_dir(&dw))
- 		{
- 			// if empty dir entry, large enough, use it
- 			if((!d->d_inode) && (d->d_rec_len >= reclen))
- 			{
- 				d->d_inode = nod;
--				node = get_nod(fs, nod);
-+				node = get_nod(fs, nod, &ni);
-+				dir_set_name(&dw, name, nlen);
-+				put_dir(&dw);
- 				node->i_links_count++;
--				d->d_name_len = nlen;
--				strncpy(d->d_name, name, nlen);
--				return;
-+				put_nod(ni);
-+				goto out;
- 			}
- 			// if entry with enough room (last one?), shrink it & use it
- 			if(d->d_rec_len >= (sizeof(directory) + rndup(d->d_name_len, 4) + reclen))
- 			{
--				reclen = d->d_rec_len;
--				d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
--				reclen -= d->d_rec_len;
--				d = (directory*) (((int8*)d) + d->d_rec_len);
--				d->d_rec_len = reclen;
--				d->d_inode = nod;
--				node = get_nod(fs, nod);
-+				d = shrink_dir(&dw, nod, name, nlen);
-+				put_dir(&dw);
-+				node = get_nod(fs, nod, &ni);
- 				node->i_links_count++;
--				d->d_name_len = nlen;
--				strncpy(d->d_name, name, nlen);
--				return;
-+				put_nod(ni);
-+				goto out;
- 			}
- 		}
-+		put_dir(&dw);
-+		lbw = bw;
- 	}
- 	// we found no free entry in the directory, so we add a block
--	if(!(b = get_workblk()))
--		error_msg_and_die("get_workblk() failed.");
--	d = (directory*)b;
--	d->d_inode = nod;
--	node = get_nod(fs, nod);
-+	node = get_nod(fs, nod, &ni);
-+	d = new_dir(fs, nod, name, nlen, &dw);
- 	node->i_links_count++;
--	d->d_rec_len = BLOCKSIZE;
--	d->d_name_len = nlen;
--	strncpy(d->d_name, name, nlen);
--	extend_blk(fs, dnod, b, 1);
--	get_nod(fs, dnod)->i_size += BLOCKSIZE;
--	free_workblk(b);
-+	put_nod(ni);
-+	next_dir(&dw); // Force the data into the buffer
-+
-+	inode_pos_init(fs, &ipos, dnod, INODE_POS_EXTEND, &lbw);
-+	extend_inode_blk(fs, &ipos, dir_data(&dw), 1);
-+	inode_pos_finish(fs, &ipos);
-+
-+	put_dir(&dw);
-+	pnode->i_size += BLOCKSIZE;
-+out:
-+	put_nod(dni);
- }
- 
- // find an entry in a directory
-@@ -1321,11 +1948,13 @@
- 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
- 	{
- 		directory *d;
--		uint8 *b;
--		b = get_blk(fs, bk);
--		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
--			if(d->d_inode && (nlen == d->d_name_len) && !strncmp(d->d_name, name, nlen))
-+		dirwalker dw;
-+		for (d = get_dir(fs, bk, &dw); d; d=next_dir(&dw))
-+			if(d->d_inode && (nlen == d->d_name_len) && !strncmp(dir_name(&dw), name, nlen)) {
-+				put_dir(&dw);
- 				return d->d_inode;
-+			}
-+		put_dir(&dw);
- 	}
- 	return 0;
- }
-@@ -1356,47 +1985,55 @@
- 	return nod;
- }
- 
-+// chmod an inode
-+void
-+chmod_fs(filesystem *fs, uint32 nod, uint16 mode, uint16 uid, uint16 gid)
-+{
-+	inode *node;
-+	nod_info *ni;
-+	node = get_nod(fs, nod, &ni);
-+	node->i_mode = (node->i_mode & ~FM_IMASK) | (mode & FM_IMASK);
-+	node->i_uid = uid;
-+	node->i_gid = gid;
-+	put_nod(ni);
-+}
-+
- // create a simple inode
- static uint32
- mknod_fs(filesystem *fs, uint32 parent_nod, const char *name, uint16 mode, uint16 uid, uint16 gid, uint8 major, uint8 minor, uint32 ctime, uint32 mtime)
- {
- 	uint32 nod;
- 	inode *node;
--	if((nod = find_dir(fs, parent_nod, name)))
--	{
--		node = get_nod(fs, nod);
--		if((node->i_mode & FM_IFMT) != (mode & FM_IFMT))
--			error_msg_and_die("node '%s' already exists and isn't of the same type", name);
--		node->i_mode = mode;
--	}
--	else
-+	nod_info *ni;
-+	gd_info *gi;
-+
-+	nod = alloc_nod(fs);
-+	node = get_nod(fs, nod, &ni);
-+	node->i_mode = mode;
-+	add2dir(fs, parent_nod, nod, name);
-+	switch(mode & FM_IFMT)
- 	{
--		nod = alloc_nod(fs);
--		node = get_nod(fs, nod);
--		node->i_mode = mode;
--		add2dir(fs, parent_nod, nod, name);
--		switch(mode & FM_IFMT)
--		{
--			case FM_IFLNK:
--				mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
--				break;
--			case FM_IFBLK:
--			case FM_IFCHR:
--				((uint8*)get_nod(fs, nod)->i_block)[0] = minor;
--				((uint8*)get_nod(fs, nod)->i_block)[1] = major;
--				break;
--			case FM_IFDIR:
--				add2dir(fs, nod, nod, ".");
--				add2dir(fs, nod, parent_nod, "..");
--				fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++;
--				break;
--		}
-+	case FM_IFLNK:
-+		mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
-+		break;
-+	case FM_IFBLK:
-+	case FM_IFCHR:
-+		((uint8*)node->i_block)[0] = minor;
-+		((uint8*)node->i_block)[1] = major;
-+		break;
-+	case FM_IFDIR:
-+		add2dir(fs, nod, nod, ".");
-+		add2dir(fs, nod, parent_nod, "..");
-+		get_gd(fs,GRP_GROUP_OF_INODE(fs,nod),&gi)->bg_used_dirs_count++;
-+		put_gd(gi);
-+		break;
- 	}
- 	node->i_uid = uid;
- 	node->i_gid = gid;
- 	node->i_atime = mtime;
- 	node->i_ctime = ctime;
- 	node->i_mtime = mtime;
-+	put_nod(ni);
- 	return nod;
- }
- 
-@@ -1413,33 +2050,73 @@
- mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 *b, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
- {
- 	uint32 nod = mknod_fs(fs, parent_nod, name, FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO, uid, gid, 0, 0, ctime, mtime);
--	extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
--	get_nod(fs, nod)->i_size = size;
--	if(size <= 4 * (EXT2_TIND_BLOCK+1))
--	{
--		strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size);
-+	nod_info *ni;
-+	inode *node = get_nod(fs, nod, &ni);
-+	inode_pos ipos;
-+
-+	inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
-+	node->i_size = size;
-+	if(size < 4 * (EXT2_TIND_BLOCK+1))
-+	{
-+		strncpy((char*)node->i_block, (char*)b, size);
-+		((char*)node->i_block)[size+1] = '\0';
-+		inode_pos_finish(fs, &ipos);
-+		put_nod(ni);
- 		return nod;
- 	}
--	extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
-+	extend_inode_blk(fs, &ipos, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
-+	inode_pos_finish(fs, &ipos);
-+	put_nod(ni);
- 	return nod;
- }
- 
-+static void
-+fs_upgrade_rev1_largefile(filesystem *fs)
-+{
-+	fs->sb->s_rev_level = 1;
-+	fs->sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
-+	fs->sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
-+}
-+
-+#define COPY_BLOCKS 16
-+#define CB_SIZE (COPY_BLOCKS * BLOCKSIZE)
-+
- // make a file from a FILE*
- static uint32
--mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
-+mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
- {
- 	uint8 * b;
- 	uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime);
--	extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
--	get_nod(fs, nod)->i_size = size;
--	if (size) {
--		if(!(b = (uint8*)calloc(rndup(size, BLOCKSIZE), 1)))
--			error_msg_and_die("not enough mem to read file '%s'", name);
--		if(f)
--			fread(b, size, 1, f); // FIXME: ugly. use mmap() ...
--		extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
--		free(b);
--	}
-+	nod_info *ni;
-+	inode *node = get_nod(fs, nod, &ni);
-+	off_t size = 0;
-+	size_t readbytes;
-+	inode_pos ipos;
-+	int fullsize;
-+
-+	b = malloc(CB_SIZE);
-+	if (!b)
-+		error_msg_and_die("mkfile_fs: out of memory");
-+	inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
-+	readbytes = fread(b, 1, CB_SIZE, f);
-+	while (readbytes) {
-+		fullsize = rndup(readbytes, BLOCKSIZE);
-+		// Fill to end of block with zeros.
-+		memset(b + readbytes, 0, fullsize - readbytes);
-+		extend_inode_blk(fs, &ipos, b, fullsize / BLOCKSIZE);
-+		size += readbytes;
-+		readbytes = fread(b, 1, CB_SIZE, f);
-+	}
-+	if (size > 0x7fffffff) {
-+		if (fs->sb->s_rev_level < 1)
-+			fs_upgrade_rev1_largefile(fs);
-+		fs->sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
-+	}
-+	node->i_dir_acl = size >> 32;
-+	node->i_size = size;
-+	inode_pos_finish(fs, &ipos);
-+	put_nod(ni);
-+	free(b);
- 	return nod;
- }
- 
-@@ -1591,13 +2268,24 @@
- 				dname = malloc(len + 1);
- 				for(i = start; i < count; i++)
- 				{
-+					uint32 oldnod;
- 					SNPRINTF(dname, len, "%s%lu", name, i);
--					mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
-+					oldnod = find_dir(fs, nod, dname);
-+					if(oldnod)
-+						chmod_fs(fs, oldnod, mode, uid, gid);
-+					else
-+						mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
- 				}
- 				free(dname);
- 			}
- 			else
--				mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
-+			{
-+				uint32 oldnod = find_dir(fs, nod, name);
-+				if(oldnod)
-+					chmod_fs(fs, oldnod, mode, uid, gid);
-+				else
-+					mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
-+			}
- 		}
- 	}
- 	if (line)
-@@ -1643,6 +2331,10 @@
- 			switch(st.st_mode & S_IFMT)
- 			{
- 				case S_IFLNK:
-+					if((st.st_mode & S_IFMT) == S_IFREG || st.st_size >= 4 * (EXT2_TIND_BLOCK+1))
-+						stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
-+					stats->ninodes++;
-+					break;
- 				case S_IFREG:
- 					if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1))
- 						stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
-@@ -1657,19 +2349,33 @@
- 					if(chdir(dent->d_name) < 0)
- 						perror_msg_and_die(dent->d_name);
- 					add2fs_from_dir(fs, this_nod, squash_uids, squash_perms, fs_timestamp, stats);
--					chdir("..");
-+					if (chdir("..") == -1)
-+						perror_msg_and_die("..");
-+
- 					break;
- 				default:
- 					break;
- 			}
- 		else
- 		{
-+			if((nod = find_dir(fs, this_nod, name)))
-+			{
-+				error_msg("ignoring duplicate entry %s", name);
-+				if(S_ISDIR(st.st_mode)) {
-+					if(chdir(dent->d_name) < 0)
-+						perror_msg_and_die(name);
-+					add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
-+					if (chdir("..") == -1)
-+						perror_msg_and_die("..");
-+				}
-+				continue;
-+			}
- 			save_nod = 0;
- 			/* Check for hardlinks */
- 			if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) {
--				int32 hdlink = is_hardlink(st.st_ino);
-+				int32 hdlink = is_hardlink(fs, st.st_ino);
- 				if (hdlink >= 0) {
--					add2dir(fs, this_nod, hdlinks.hdl[hdlink].dst_nod, name);
-+					add2dir(fs, this_nod, fs->hdlinks.hdl[hdlink].dst_nod, name);
- 					continue;
- 				} else {
- 					save_nod = 1;
-@@ -1697,8 +2403,12 @@
- 					free(lnk);
- 					break;
- 				case S_IFREG:
--					fh = xfopen(dent->d_name, "rb");
--					nod = mkfile_fs(fs, this_nod, name, mode, st.st_size, fh, uid, gid, ctime, mtime);
-+					fh = fopen(dent->d_name, "rb");
-+					if (!fh) {
-+						error_msg("Unable to open file %s", dent->d_name);
-+						break;
-+					}
-+					nod = mkfile_fs(fs, this_nod, name, mode, fh, uid, gid, ctime, mtime);
- 					fclose(fh);
- 					break;
- 				case S_IFDIR:
-@@ -1706,199 +2416,128 @@
- 					if(chdir(dent->d_name) < 0)
- 						perror_msg_and_die(name);
- 					add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
--					chdir("..");
-+					if (chdir("..") == -1)
-+						perror_msg_and_die("..");
- 					break;
- 				default:
- 					error_msg("ignoring entry %s", name);
- 			}
- 			if (save_nod) {
--				if (hdlinks.count == hdlink_cnt) {
--					if ((hdlinks.hdl = 
--						 realloc (hdlinks.hdl, (hdlink_cnt + HDLINK_CNT) *
-+				if (fs->hdlinks.count == fs->hdlink_cnt) {
-+					if ((fs->hdlinks.hdl =
-+						 realloc (fs->hdlinks.hdl, (fs->hdlink_cnt + HDLINK_CNT) *
- 								  sizeof (struct hdlink_s))) == NULL) {
- 						error_msg_and_die("Not enough memory");
- 					}
--					hdlink_cnt += HDLINK_CNT;
-+					fs->hdlink_cnt += HDLINK_CNT;
- 				}
--				hdlinks.hdl[hdlinks.count].src_inode = st.st_ino;
--				hdlinks.hdl[hdlinks.count].dst_nod = nod;
--				hdlinks.count++;
-+				fs->hdlinks.hdl[fs->hdlinks.count].src_inode = st.st_ino;
-+				fs->hdlinks.hdl[fs->hdlinks.count].dst_nod = nod;
-+				fs->hdlinks.count++;
- 			}
- 		}
- 	}
- 	closedir(dh);
- }
- 
--// endianness swap of x-indirect blocks
-+// Copy size blocks from src to dst, putting holes in the output
-+// file (if possible) if the input block is all zeros.
-+// Copy size blocks from src to dst, putting holes in the output
-+// file (if possible) if the input block is all zeros.
- static void
--swap_goodblocks(filesystem *fs, inode *nod)
-+copy_file(filesystem *fs, FILE *dst, FILE *src, size_t size)
- {
--	uint32 i,j;
--	int done=0;
--	uint32 *b,*b2;
-+	uint8 *b;
- 
--	uint32 nblk = nod->i_blocks / INOBLK;
--	if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
--		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
--			nod->i_block[i] = swab32(nod->i_block[i]);
--	if(nblk <= EXT2_IND_BLOCK)
--		return;
--	swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
--	if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
--		return;
--	/* Currently this will fail b'cos the number of blocks as stored
--	   in i_blocks also includes the indirection blocks (see
--	   walk_bw). But this function assumes that i_blocks only
--	   stores the count of data blocks ( Actually according to
--	   "Understanding the Linux Kernel" (Table 17-3 p502 1st Ed)
--	   i_blocks IS supposed to store the count of data blocks). so
--	   with a file of size 268K nblk would be 269.The above check
--	   will be false even though double indirection hasn't been
--	   started.This is benign as 0 means block 0 which has been
--	   zeroed out and therefore points back to itself from any offset
--	 */
--	// FIXME: I have fixed that, but I have the feeling the rest of
--	// ths function needs to be fixed for the same reasons - Xav
--	assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
--	for(i = 0; i < BLOCKSIZE/4; i++)
--		if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
--			swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
--	swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
--	if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
--		return;
--	/* Adding support for triple indirection */
--	b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
--	for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
--		b2 = (uint32*)get_blk(fs,b[i]); 
--		for(j=0; j<BLOCKSIZE/4;j++) {
--			if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + 
--				     (BLOCKSIZE/4)*(BLOCKSIZE/4) + 
--				     i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + 
--				     j*(BLOCKSIZE/4)) ) 
--			  swap_block(get_blk(fs,b2[j]));
--			else {
--			  done = 1;
--			  break;
--			}
-+	b = malloc(BLOCKSIZE);
-+	if (!b)
-+		error_msg_and_die("copy_file: out of memory");
-+	if (fseek(src, 0, SEEK_SET))
-+		perror_msg_and_die("fseek");
-+	if (ftruncate(fileno(dst), 0))
-+		perror_msg_and_die("copy_file: ftruncate");
-+	while (size > 0) {
-+		if (fread(b, BLOCKSIZE, 1, src) != 1)
-+			perror_msg_and_die("copy failed on read");
-+		if ((dst != stdout) && fs->holes && is_blk_empty(b)) {
-+			/* Empty block, just skip it */
-+			if (fseek(dst, BLOCKSIZE, SEEK_CUR))
-+				perror_msg_and_die("fseek");
-+		} else {
-+			if (fwrite(b, BLOCKSIZE, 1, dst) != 1)
-+				perror_msg_and_die("copy failed on write");
- 		}
--		swap_block((uint8 *)b2);
-+		size--;
- 	}
--	swap_block((uint8 *)b);
--	return;
-+	free(b);
- }
- 
--static void
--swap_badblocks(filesystem *fs, inode *nod)
-+// Allocate a new filesystem structure, allocate internal memory,
-+// and initialize the contents.
-+static filesystem *
-+alloc_fs(int swapit, char *fname, uint32 nbblocks, FILE *srcfile)
- {
--	uint32 i,j;
--	int done=0;
--	uint32 *b,*b2;
-+	filesystem *fs;
-+	struct stat srcstat, dststat;
- 
--	uint32 nblk = nod->i_blocks / INOBLK;
--	if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
--		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
--			nod->i_block[i] = swab32(nod->i_block[i]);
--	if(nblk <= EXT2_IND_BLOCK)
--		return;
--	swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
--	if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
--		return;
--	/* See comment in swap_goodblocks */
--	assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
--	swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
--	for(i = 0; i < BLOCKSIZE/4; i++)
--		if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
--			swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
--	if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
--		return;
--	/* Adding support for triple indirection */
--	b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
--	swap_block((uint8 *)b);
--	for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
--		b2 = (uint32*)get_blk(fs,b[i]); 
--		swap_block((uint8 *)b2);
--		for(j=0; j<BLOCKSIZE/4;j++) {
--			if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + 
--				     (BLOCKSIZE/4)*(BLOCKSIZE/4) + 
--				     i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + 
--				     j*(BLOCKSIZE/4)) ) 
--			  swap_block(get_blk(fs,b2[j]));
--			else {
--			  done = 1;
--			  break;
--			}
--		}
--	}
--	return;
--}
-+	fs = malloc(sizeof(*fs));
-+	if (!fs)
-+		error_msg_and_die("not enough memory for filesystem");
-+	memset(fs, 0, sizeof(*fs));
-+	fs->swapit = swapit;
-+	cache_init(&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed);
-+	cache_init(&fs->gds, MAX_FREE_CACHE_GDS, gd_elem_val, gd_freed);
-+	cache_init(&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS,
-+		   blkmap_elem_val, blkmap_freed);
-+	cache_init(&fs->inodes, MAX_FREE_CACHE_INODES,
-+		   inode_elem_val, inode_freed);
-+	fs->hdlink_cnt = HDLINK_CNT;
-+	fs->hdlinks.hdl = calloc(sizeof(struct hdlink_s), fs->hdlink_cnt);
-+	if (!fs->hdlinks.hdl)
-+		error_msg_and_die("Not enough memory");
-+	fs->hdlinks.count = 0 ;
- 
--// endianness swap of the whole filesystem
--static void
--swap_goodfs(filesystem *fs)
--{
--	uint32 i;
--	for(i = 1; i < fs->sb.s_inodes_count; i++)
--	{
--		inode *nod = get_nod(fs, i);
--		if(nod->i_mode & FM_IFDIR)
--		{
--			blockwalker bw;
--			uint32 bk;
--			init_bw(&bw);
--			while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
--			{
--				directory *d;
--				uint8 *b;
--				b = get_blk(fs, bk);
--				for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + swab16(d->d_rec_len)))
--					swap_dir(d);
--			}
--		}
--		swap_goodblocks(fs, nod);
--		swap_nod(nod);
--	}
--	for(i=0;i<GRP_NBGROUPS(fs);i++)
--		swap_gd(&(fs->gd[i]));
--	swap_sb(&fs->sb);
-+	if (strcmp(fname, "-") == 0)
-+		fs->f = tmpfile();
-+	else if (srcfile) {
-+		if (fstat(fileno(srcfile), &srcstat))
-+			perror_msg_and_die("fstat srcfile");
-+		if (stat(fname, &dststat) == 0
-+		    && srcstat.st_ino == dststat.st_ino
-+		    && srcstat.st_dev == dststat.st_dev)
-+		  {
-+			// source and destination are the same file, don't
-+			// truncate or copy, just use the file.
-+			fs->f = fopen(fname, "r+b");
-+		} else {
-+			fs->f = fopen(fname, "w+b");
-+			if (fs->f)
-+				copy_file(fs, fs->f, srcfile, nbblocks);
-+		}
-+	} else
-+		fs->f = fopen(fname, "w+b");
-+	if (!fs->f)
-+		perror_msg_and_die("opening %s", fname);
-+	return fs;
- }
- 
-+/* Make sure the output file is the right size */
- static void
--swap_badfs(filesystem *fs)
-+set_file_size(filesystem *fs)
- {
--	uint32 i;
--	swap_sb(&fs->sb);
--	for(i=0;i<GRP_NBGROUPS(fs);i++)
--		swap_gd(&(fs->gd[i]));
--	for(i = 1; i < fs->sb.s_inodes_count; i++)
--	{
--		inode *nod = get_nod(fs, i);
--		swap_nod(nod);
--		swap_badblocks(fs, nod);
--		if(nod->i_mode & FM_IFDIR)
--		{
--			blockwalker bw;
--			uint32 bk;
--			init_bw(&bw);
--			while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
--			{
--				directory *d;
--				uint8 *b;
--				b = get_blk(fs, bk);
--				for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
--					swap_dir(d);
--			}
--		}
--	}
-+	if (ftruncate(fileno(fs->f),
-+		      ((off_t) fs->sb->s_blocks_count) * BLOCKSIZE))
-+		perror_msg_and_die("set_file_size: ftruncate");
- }
- 
- // initialize an empty filesystem
- static filesystem *
--init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp)
-+init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes,
-+	uint32 fs_timestamp, uint32 creator_os, int swapit, char *fname)
- {
- 	uint32 i;
- 	filesystem *fs;
--	directory *d;
--	uint8 * b;
-+	dirwalker dw;
- 	uint32 nod, first_block;
- 	uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks,
- 		free_blocks_per_group,nbblocks_per_group,min_nbgroups;
-@@ -1906,6 +2545,11 @@
- 	uint32 j;
- 	uint8 *bbm,*ibm;
- 	inode *itab0;
-+	blk_info *bi;
-+	nod_info *ni;
-+	groupdescriptor *gd;
-+	gd_info *gi;
-+	inode_pos ipos;
- 	
- 	if(nbresrvd < 0)
- 		error_msg_and_die("reserved blocks value is invalid. Note: options have changed, see --help or the man page.");
-@@ -1919,10 +2563,14 @@
- 	 */
- 	min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
- 
-+	/* On filesystems with 1k block size, the bootloader area uses a full
-+	 * block. For 2048 and up, the superblock can be fitted into block 0.
-+	 */
-+	first_block = (BLOCKSIZE == 1024);
-+
- 	/* nbblocks is the total number of blocks in the filesystem.
- 	 * a block group can have no more than 8192 blocks.
- 	 */
--	first_block = (BLOCKSIZE == 1024);
- 	nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP;
- 	if(nbgroups < min_nbgroups) nbgroups = min_nbgroups;
- 	nbblocks_per_group = rndup((nbblocks - first_block + nbgroups - 1)/nbgroups, 8);
-@@ -1934,51 +2582,59 @@
- 	gdsz = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
- 	itblsz = nbinodes_per_group * sizeof(inode)/BLOCKSIZE;
- 	overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz;
--	if((uint32)nbblocks - 1 < overhead_per_group * nbgroups)
--		error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
--	free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/;
-+	free_blocks = nbblocks - overhead_per_group*nbgroups - first_block;
- 	free_blocks_per_group = nbblocks_per_group - overhead_per_group;
-+	if(free_blocks < 0)
-+		error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
- 
--	if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
--		error_msg_and_die("not enough memory for filesystem");
-+	fs = alloc_fs(swapit, fname, nbblocks, NULL);
-+	fs->sb = calloc(1, SUPERBLOCK_SIZE);
-+	if (!fs->sb)
-+		error_msg_and_die("error allocating header memory");
- 
- 	// create the superblock for an empty filesystem
--	fs->sb.s_inodes_count = nbinodes_per_group * nbgroups;
--	fs->sb.s_blocks_count = nbblocks;
--	fs->sb.s_r_blocks_count = nbresrvd;
--	fs->sb.s_free_blocks_count = free_blocks;
--	fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
--	fs->sb.s_first_data_block = first_block;
--	fs->sb.s_log_block_size = BLOCKSIZE >> 11;
--	fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
--	fs->sb.s_blocks_per_group = nbblocks_per_group;
--	fs->sb.s_frags_per_group = nbblocks_per_group;
--	fs->sb.s_inodes_per_group = nbinodes_per_group;
--	fs->sb.s_wtime = fs_timestamp;
--	fs->sb.s_magic = EXT2_MAGIC_NUMBER;
--	fs->sb.s_lastcheck = fs_timestamp;
-+	fs->sb->s_inodes_count = nbinodes_per_group * nbgroups;
-+	fs->sb->s_blocks_count = nbblocks;
-+	fs->sb->s_r_blocks_count = nbresrvd;
-+	fs->sb->s_free_blocks_count = free_blocks;
-+	fs->sb->s_free_inodes_count = fs->sb->s_inodes_count - EXT2_FIRST_INO + 1;
-+	fs->sb->s_first_data_block = first_block;
-+	fs->sb->s_log_block_size = BLOCKSIZE >> 11;
-+	fs->sb->s_log_frag_size = BLOCKSIZE >> 11;
-+	fs->sb->s_blocks_per_group = nbblocks_per_group;
-+	fs->sb->s_frags_per_group = nbblocks_per_group;
-+	fs->sb->s_inodes_per_group = nbinodes_per_group;
-+	fs->sb->s_wtime = fs_timestamp;
-+	fs->sb->s_magic = EXT2_MAGIC_NUMBER;
-+	fs->sb->s_lastcheck = fs_timestamp;
-+	fs->sb->s_creator_os = creator_os;
-+
-+	set_file_size(fs);
- 
- 	// set up groupdescriptors
--	for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1;
-+	for(i=0, bbmpos=first_block+1+gdsz, ibmpos=bbmpos+1, itblpos=ibmpos+1;
- 		i<nbgroups;
- 		i++, bbmpos+=nbblocks_per_group, ibmpos+=nbblocks_per_group, itblpos+=nbblocks_per_group)
- 	{
-+		gd = get_gd(fs, i, &gi);
-+
- 		if(free_blocks > free_blocks_per_group) {
--			fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
-+			gd->bg_free_blocks_count = free_blocks_per_group;
- 			free_blocks -= free_blocks_per_group;
- 		} else {
--			fs->gd[i].bg_free_blocks_count = free_blocks;
-+			gd->bg_free_blocks_count = free_blocks;
- 			free_blocks = 0; // this is the last block group
- 		}
- 		if(i)
--			fs->gd[i].bg_free_inodes_count = nbinodes_per_group;
-+			gd->bg_free_inodes_count = nbinodes_per_group;
- 		else
--			fs->gd[i].bg_free_inodes_count = nbinodes_per_group -
-+			gd->bg_free_inodes_count = nbinodes_per_group -
- 							EXT2_FIRST_INO + 2;
--		fs->gd[i].bg_used_dirs_count = 0;
--		fs->gd[i].bg_block_bitmap = bbmpos;
--		fs->gd[i].bg_inode_bitmap = ibmpos;
--		fs->gd[i].bg_inode_table = itblpos;
-+		gd->bg_used_dirs_count = 0;
-+		gd->bg_block_bitmap = bbmpos;
-+		gd->bg_inode_bitmap = ibmpos;
-+		gd->bg_inode_table = itblpos;
-+		put_gd(gi);
- 	}
- 
- 	/* Mark non-filesystem blocks and inodes as allocated */
-@@ -1984,110 +2640,143 @@
- 	/* Mark non-filesystem blocks and inodes as allocated */
- 	/* Mark system blocks and inodes as allocated         */
- 	for(i = 0; i<nbgroups;i++) {
--
- 		/* Block bitmap */
--		bbm = get_blk(fs,fs->gd[i].bg_block_bitmap);	
-+		gd = get_gd(fs, i, &gi);
-+		bbm = GRP_GET_GROUP_BBM(fs, gd, &bi);
- 		//non-filesystem blocks
--		for(j = fs->gd[i].bg_free_blocks_count
-+		for(j = gd->bg_free_blocks_count
- 		        + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++)
- 			allocate(bbm, j); 
- 		//system blocks
- 		for(j = 1; j <= overhead_per_group; j++)
- 			allocate(bbm, j); 
--		
-+		GRP_PUT_GROUP_BBM(bi);
-+
- 		/* Inode bitmap */
--		ibm = get_blk(fs,fs->gd[i].bg_inode_bitmap);	
-+		ibm = GRP_GET_GROUP_IBM(fs, gd, &bi);
- 		//non-filesystem inodes
--		for(j = fs->sb.s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
-+		for(j = fs->sb->s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
- 			allocate(ibm, j);
- 
- 		//system inodes
- 		if(i == 0)
- 			for(j = 1; j < EXT2_FIRST_INO; j++)
- 				allocate(ibm, j);
-+		GRP_PUT_GROUP_IBM(bi);
-+		put_gd(gi);
- 	}
- 
- 	// make root inode and directory
- 	/* We have groups now. Add the root filesystem in group 0 */
- 	/* Also increment the directory count for group 0 */
--	fs->gd[0].bg_free_inodes_count--;
--	fs->gd[0].bg_used_dirs_count = 1;
--	itab0 = (inode *)get_blk(fs,fs->gd[0].bg_inode_table);
--	itab0[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH; 
--	itab0[EXT2_ROOT_INO-1].i_ctime = fs_timestamp;
--	itab0[EXT2_ROOT_INO-1].i_mtime = fs_timestamp;
--	itab0[EXT2_ROOT_INO-1].i_atime = fs_timestamp;
--	itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
--	itab0[EXT2_ROOT_INO-1].i_links_count = 2;
--
--	if(!(b = get_workblk()))
--		error_msg_and_die("get_workblk() failed.");
--	d = (directory*)b;
--	d->d_inode = EXT2_ROOT_INO;
--	d->d_rec_len = sizeof(directory)+4;
--	d->d_name_len = 1;
--	strcpy(d->d_name, ".");
--	d = (directory*)(b + d->d_rec_len);
--	d->d_inode = EXT2_ROOT_INO;
--	d->d_rec_len = BLOCKSIZE - (sizeof(directory)+4);
--	d->d_name_len = 2;
--	strcpy(d->d_name, "..");
--	extend_blk(fs, EXT2_ROOT_INO, b, 1);
-+	gd = get_gd(fs, 0, &gi);
-+	gd->bg_free_inodes_count--;
-+	gd->bg_used_dirs_count = 1;
-+	put_gd(gi);
-+	itab0 = get_nod(fs, EXT2_ROOT_INO, &ni);
-+	itab0->i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH;
-+	itab0->i_ctime = fs_timestamp;
-+	itab0->i_mtime = fs_timestamp;
-+	itab0->i_atime = fs_timestamp;
-+	itab0->i_size = BLOCKSIZE;
-+	itab0->i_links_count = 2;
-+	put_nod(ni);
-+
-+	new_dir(fs, EXT2_ROOT_INO, ".", 1, &dw);
-+	shrink_dir(&dw, EXT2_ROOT_INO, "..", 2);
-+	next_dir(&dw); // Force the data into the buffer
-+	inode_pos_init(fs, &ipos, EXT2_ROOT_INO, INODE_POS_EXTEND, NULL);
-+	extend_inode_blk(fs, &ipos, dir_data(&dw), 1);
-+	inode_pos_finish(fs, &ipos);
-+	put_dir(&dw);
- 
--	// make lost+found directory and reserve blocks
--	if(fs->sb.s_r_blocks_count)
-+	// make lost+found directory
-+	if(fs->sb->s_r_blocks_count)
- 	{
--		nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
-+		inode *node;
-+		uint8 *b;
-+
-+		nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU,
-+			       0, 0, fs_timestamp, fs_timestamp);
-+		b = get_workblk();
- 		memset(b, 0, BLOCKSIZE);
- 		((directory*)b)->d_rec_len = BLOCKSIZE;
--		/* We run into problems with e2fsck if directory lost+found grows
--		 * bigger than this. Need to find out why this happens - sundar
--		 */
--		if (fs->sb.s_r_blocks_count > fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS ) 
--			fs->sb.s_r_blocks_count = fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS;
--		for(i = 1; i < fs->sb.s_r_blocks_count; i++)
--			extend_blk(fs, nod, b, 1);
--		get_nod(fs, nod)->i_size = fs->sb.s_r_blocks_count * BLOCKSIZE;
-+		inode_pos_init(fs, &ipos, nod, INODE_POS_EXTEND, NULL);
-+		// It is always 16 blocks to start out with
-+		for(i = 1; i < 16; i++)
-+			extend_inode_blk(fs, &ipos, b, 1);
-+		inode_pos_finish(fs, &ipos);
-+		free_workblk(b);
-+		node = get_nod(fs, nod, &ni);
-+		node->i_size = 16 * BLOCKSIZE;
-+		put_nod(ni);
- 	}
--	free_workblk(b);
- 
- 	// administrative info
--	fs->sb.s_state = 1;
--	fs->sb.s_max_mnt_count = 20;
-+	fs->sb->s_state = 1;
-+	fs->sb->s_max_mnt_count = 20;
- 
- 	// options for me
--	if(holes)
--		fs->sb.s_reserved[200] |= OP_HOLES;
-+	fs->holes = holes;
- 	
- 	return fs;
- }
- 
- // loads a filesystem from disk
- static filesystem *
--load_fs(FILE * fh, int swapit)
-+load_fs(FILE *fh, int swapit, char *fname)
- {
--	size_t fssize;
-+	off_t fssize;
- 	filesystem *fs;
--	if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1))
-+
-+	if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftello(fh)) == -1))
- 		perror_msg_and_die("input filesystem image");
- 	rewind(fh);
--	fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
-+	if ((fssize % BLOCKSIZE) != 0)
-+		error_msg_and_die("Input file not a multiple of block size");
-+	fssize /= BLOCKSIZE;
- 	if(fssize < 16) // totally arbitrary
- 		error_msg_and_die("too small filesystem");
--	if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
--		error_msg_and_die("not enough memory for filesystem");
--	if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
--		perror_msg_and_die("input filesystem image");
-+	fs = alloc_fs(swapit, fname, fssize, fh);
-+
-+	/* Read and check the superblock, then read the superblock
-+	 * and all the group descriptors */
-+	fs->sb = malloc(SUPERBLOCK_SIZE);
-+	if (!fs->sb)
-+		error_msg_and_die("error allocating header memory");
-+	if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET))
-+		perror_msg_and_die("fseek");
-+	if (fread(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1)
-+		perror_msg_and_die("fread filesystem image superblock");
- 	if(swapit)
--		swap_badfs(fs);
--	if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
-+		swap_sb(fs->sb);
-+
-+	if((fs->sb->s_rev_level > 1) || (fs->sb->s_magic != EXT2_MAGIC_NUMBER))
- 		error_msg_and_die("not a suitable ext2 filesystem");
-+	if (fs->sb->s_rev_level > 0) {
-+		if (fs->sb->s_first_ino != EXT2_GOOD_OLD_FIRST_INO)
-+			error_msg_and_die("First inode incompatible");
-+		if (fs->sb->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE)
-+			error_msg_and_die("inode size incompatible");
-+		if (fs->sb->s_feature_compat)
-+			error_msg_and_die("Unsupported compat features");
-+		if (fs->sb->s_feature_incompat)
-+			error_msg_and_die("Unsupported incompat features");
-+		if (fs->sb->s_feature_ro_compat
-+		    & ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
-+			error_msg_and_die("Unsupported ro compat features");
-+	}
-+
-+	set_file_size(fs);
- 	return fs;
- }
- 
- static void
- free_fs(filesystem *fs)
- {
-+	free(fs->hdlinks.hdl);
-+	fclose(fs->f);
-+	free(fs->sb);
- 	free(fs);
- }
- 
-@@ -2123,16 +2812,23 @@
- {
- 	blockwalker bw;
- 	uint32 bk;
--	int32 fsize = get_nod(fs, nod)->i_size;
-+	nod_info *ni;
-+	inode *node = get_nod(fs, nod, &ni);
-+	int32 fsize = node->i_size;
-+	blk_info *bi;
-+
- 	init_bw(&bw);
- 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
- 	{
- 		if(fsize <= 0)
- 			error_msg_and_die("wrong size while saving inode %d", nod);
--		if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
-+		if(fwrite(get_blk(fs, bk, &bi),
-+			  (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
- 			error_msg_and_die("error while saving inode %d", nod);
-+		put_blk(bi);
- 		fsize -= BLOCKSIZE;
- 	}
-+	put_nod(ni);
- }
- 
- 
-@@ -2141,8 +2837,11 @@
- print_dev(filesystem *fs, uint32 nod)
- {
- 	int minor, major;
--	minor = ((uint8*)get_nod(fs, nod)->i_block)[0];
--	major = ((uint8*)get_nod(fs, nod)->i_block)[1];
-+	nod_info *ni;
-+	inode *node = get_nod(fs, nod, &ni);
-+	minor = ((uint8*)node->i_block)[0];
-+	major = ((uint8*)node->i_block)[1];
-+	put_nod(ni);
- 	printf("major: %d, minor: %d\n", major, minor);
- }
- 
-@@ -2157,17 +2856,15 @@
- 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
- 	{
- 		directory *d;
--		uint8 *b;
--		b = get_blk(fs, bk);
--		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
-+		dirwalker dw;
-+		for (d = get_dir(fs, bk, &dw); d; d = next_dir(&dw))
- 			if(d->d_inode)
- 			{
--				int i;
- 				printf("entry '");
--				for(i = 0; i < d->d_name_len; i++)
--					putchar(d->d_name[i]);
-+				fwrite(dir_name(&dw), 1, d->d_name_len, stdout);
- 				printf("' (inode %d): rec_len: %d (name_len: %d)\n", d->d_inode, d->d_rec_len, d->d_name_len);
- 			}
-+		put_dir(&dw);
- 	}
- }
- 
-@@ -2175,14 +2872,18 @@
- static void
- print_link(filesystem *fs, uint32 nod)
- {
--	if(!get_nod(fs, nod)->i_blocks)
--		printf("links to '%s'\n", (char*)get_nod(fs, nod)->i_block);
-+	nod_info *ni;
-+	inode *node = get_nod(fs, nod, &ni);
-+
-+	if(!node->i_blocks)
-+		printf("links to '%s'\n", (char*)node->i_block);
- 	else
- 	{
- 		printf("links to '");
- 		write_blocks(fs, nod, stdout);
- 		printf("'\n");
- 	}
-+	put_nod(ni);
- }
- 
- // make a ls-like printout of permissions
-@@ -2251,8 +2952,13 @@
- {
- 	char *s;
- 	char perms[11];
--	if(!get_nod(fs, nod)->i_mode)
--		return;
-+	nod_info *ni;
-+	inode *node = get_nod(fs, nod, &ni);
-+	blk_info *bi;
-+	gd_info *gi;
-+
-+	if(!node->i_mode)
-+		goto out;
- 	switch(nod)
- 	{
- 		case EXT2_BAD_INO:
-@@ -2274,15 +2980,18 @@
- 		default:
- 			s = (nod >= EXT2_FIRST_INO) ? "normal" : "unknown reserved"; 
- 	}
--	printf("inode %d (%s, %d links): ", nod, s, get_nod(fs, nod)->i_links_count);
--	if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod)))
-+	printf("inode %d (%s, %d links): ", nod, s, node->i_links_count);
-+	if(!allocated(GRP_GET_INODE_BITMAP(fs,nod,&bi,&gi), GRP_IBM_OFFSET(fs,nod)))
- 	{
-+		GRP_PUT_INODE_BITMAP(bi,gi);
- 		printf("unallocated\n");
--		return;
-+		goto out;
- 	}
--	make_perms(get_nod(fs, nod)->i_mode, perms);
--	printf("%s,  size: %d byte%s (%d block%s)\n", perms, plural(get_nod(fs, nod)->i_size), plural(get_nod(fs, nod)->i_blocks / INOBLK));
--	switch(get_nod(fs, nod)->i_mode & FM_IFMT)
-+	GRP_PUT_INODE_BITMAP(bi,gi);
-+	make_perms(node->i_mode, perms);
-+	printf("%s,  size: %d byte%s (%d block%s)\n", perms,
-+	       plural(node->i_size), plural(node->i_blocks / INOBLK));
-+	switch(node->i_mode & FM_IFMT)
- 	{
- 		case FM_IFSOCK:
- 			list_blocks(fs, nod);
-@@ -2310,6 +3019,8 @@
- 			list_blocks(fs, nod);
- 	}
- 	printf("Done with inode %d\n",nod);
-+out:
-+	put_nod(ni);
- }
- 
- // describes various fields in a filesystem
-@@ -2317,49 +3028,65 @@
- print_fs(filesystem *fs)
- {
- 	uint32 i;
-+	blk_info *bi;
-+	groupdescriptor *gd;
-+	gd_info *gi;
- 	uint8 *ibm;
- 
- 	printf("%d blocks (%d free, %d reserved), first data block: %d\n",
--	       fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
--	       fs->sb.s_r_blocks_count, fs->sb.s_first_data_block);
--	printf("%d inodes (%d free)\n", fs->sb.s_inodes_count,
--	       fs->sb.s_free_inodes_count);
-+	       fs->sb->s_blocks_count, fs->sb->s_free_blocks_count,
-+	       fs->sb->s_r_blocks_count, fs->sb->s_first_data_block);
-+	printf("%d inodes (%d free)\n", fs->sb->s_inodes_count,
-+	       fs->sb->s_free_inodes_count);
- 	printf("block size = %d, frag size = %d\n",
--	       fs->sb.s_log_block_size ? (fs->sb.s_log_block_size << 11) : 1024,
--	       fs->sb.s_log_frag_size ? (fs->sb.s_log_frag_size << 11) : 1024);
-+	       fs->sb->s_log_block_size ? (fs->sb->s_log_block_size << 11) : 1024,
-+	       fs->sb->s_log_frag_size ? (fs->sb->s_log_frag_size << 11) : 1024);
- 	printf("number of groups: %d\n",GRP_NBGROUPS(fs));
- 	printf("%d blocks per group,%d frags per group,%d inodes per group\n",
--	     fs->sb.s_blocks_per_group, fs->sb.s_frags_per_group,
--	     fs->sb.s_inodes_per_group);
-+	     fs->sb->s_blocks_per_group, fs->sb->s_frags_per_group,
-+	     fs->sb->s_inodes_per_group);
- 	printf("Size of inode table: %d blocks\n",
--		(int)(fs->sb.s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
-+		(int)(fs->sb->s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
- 	for (i = 0; i < GRP_NBGROUPS(fs); i++) {
- 		printf("Group No: %d\n", i+1);
-+		gd = get_gd(fs, i, &gi);
- 		printf("block bitmap: block %d,inode bitmap: block %d, inode table: block %d\n",
--		     fs->gd[i].bg_block_bitmap, fs->gd[i].bg_inode_bitmap,
--		     fs->gd[i].bg_inode_table);
-+		     gd->bg_block_bitmap,
-+		     gd->bg_inode_bitmap,
-+		     gd->bg_inode_table);
- 		printf("block bitmap allocation:\n");
--		print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group);
-+		print_bm(GRP_GET_GROUP_BBM(fs, gd, &bi),fs->sb->s_blocks_per_group);
-+		GRP_PUT_GROUP_BBM(bi);
- 		printf("inode bitmap allocation:\n");
--		ibm = GRP_GET_GROUP_IBM(fs, i);
--		print_bm(ibm, fs->sb.s_inodes_per_group);
--		for (i = 1; i <= fs->sb.s_inodes_per_group; i++)
-+		ibm = GRP_GET_GROUP_IBM(fs, gd, &bi);
-+		print_bm(ibm, fs->sb->s_inodes_per_group);
-+		for (i = 1; i <= fs->sb->s_inodes_per_group; i++)
- 			if (allocated(ibm, i))
- 				print_inode(fs, i);
-+		GRP_PUT_GROUP_IBM(bi);
-+		put_gd(gi);
- 	}
- }
- 
- static void
--dump_fs(filesystem *fs, FILE * fh, int swapit)
-+finish_fs(filesystem *fs)
- {
--	uint32 nbblocks = fs->sb.s_blocks_count;
--	fs->sb.s_reserved[200] = 0;
--	if(swapit)
--		swap_goodfs(fs);
--	if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
--		perror_msg_and_die("output filesystem image");
--	if(swapit)
--		swap_badfs(fs);
-+	if (cache_flush(&fs->inodes))
-+		error_msg_and_die("entry mismatch on inode cache flush");
-+	if (cache_flush(&fs->blkmaps))
-+		error_msg_and_die("entry mismatch on blockmap cache flush");
-+	if (cache_flush(&fs->gds))
-+		error_msg_and_die("entry mismatch on gd cache flush");
-+	if (cache_flush(&fs->blks))
-+		error_msg_and_die("entry mismatch on block cache flush");
-+	if(fs->swapit)
-+		swap_sb(fs->sb);
-+	if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET))
-+		perror_msg_and_die("fseek");
-+	if(fwrite(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1)
-+		perror_msg_and_die("output filesystem superblock");
-+	if(fs->swapit)
-+		swap_sb(fs->sb);
- }
- 
- static void
-@@ -2419,10 +3146,12 @@
- 	"  -x, --starting-image <image>\n"
- 	"  -d, --root <directory>\n"
- 	"  -D, --devtable <file>\n"
-+	"  -B, --block-size <bytes>\n"
- 	"  -b, --size-in-blocks <blocks>\n"
- 	"  -i, --bytes-per-inode <bytes per inode>\n"
- 	"  -N, --number-of-inodes <number of inodes>\n"
- 	"  -m, --reserved-percentage <percentage of blocks to reserve>\n"
-+	"  -o, --creator-os <os>      'linux' (default), 'hurd', 'freebsd' or number.\n"
- 	"  -g, --block-map <path>     Generate a block map file for this path.\n"
- 	"  -e, --fill-value <value>   Fill unallocated blocks with value.\n"
- 	"  -z, --allow-holes          Allow files with holes.\n"
-@@ -2444,15 +3173,34 @@
- extern char* optarg;
- extern int optind, opterr, optopt;
- 
-+// parse the value for -o <os>
-+int
-+lookup_creator_os(const char *name)
-+{
-+        if (isdigit (*name))
-+                return atoi(name);
-+        else if (strcasecmp(name, "linux") == 0)
-+                return EXT2_OS_LINUX;
-+        else if (strcasecmp(name, "GNU") == 0 || strcasecmp(name, "hurd") == 0)
-+                return EXT2_OS_HURD;
-+        else if (strcasecmp(name, "freebsd") == 0)
-+                return EXT2_OS_FREEBSD;
-+        else if (strcasecmp(name, "lites") == 0)
-+                return EXT2_OS_LITES;
-+        else
-+                return EXT2_OS_LINUX;
-+}
-+
- int
- main(int argc, char **argv)
- {
--	int nbblocks = -1;
-+	long long nbblocks = -1;
- 	int nbinodes = -1;
- 	int nbresrvd = -1;
- 	float bytes_per_inode = -1;
- 	float reserved_frac = -1;
- 	int fs_timestamp = -1;
-+	int creator_os = CREATOR_OS;
- 	char * fsout = "-";
- 	char * fsin = 0;
- 	char * dopt[MAX_DOPT];
-@@ -2466,6 +3214,7 @@
- 	int squash_perms = 0;
- 	uint16 endian = 1;
- 	int bigendian = !*(char*)&endian;
-+	char *volumelabel = NULL;
- 	filesystem *fs;
- 	int i;
- 	int c;
-@@ -2476,13 +3225,16 @@
- 	  { "starting-image",	required_argument,	NULL, 'x' },
- 	  { "root",		required_argument,	NULL, 'd' },
- 	  { "devtable",		required_argument,	NULL, 'D' },
-+	  { "block-size",	required_argument,	NULL, 'B' },
- 	  { "size-in-blocks",	required_argument,	NULL, 'b' },
- 	  { "bytes-per-inode",	required_argument,	NULL, 'i' },
- 	  { "number-of-inodes",	required_argument,	NULL, 'N' },
-+	  { "volume-label",     required_argument,      NULL, 'L' },
- 	  { "reserved-percentage", required_argument,	NULL, 'm' },
-+	  { "creator-os",	required_argument,	NULL, 'o' },
- 	  { "block-map",	required_argument,	NULL, 'g' },
- 	  { "fill-value",	required_argument,	NULL, 'e' },
--	  { "allow-holes",	no_argument, 		NULL, 'z' },
-+	  { "allow-holes",	no_argument,		NULL, 'z' },
- 	  { "faketime",		no_argument,		NULL, 'f' },
- 	  { "squash",		no_argument,		NULL, 'q' },
- 	  { "squash-uids",	no_argument,		NULL, 'U' },
-@@ -2495,11 +3247,11 @@
- 
- 	app_name = argv[0];
- 
--	while((c = getopt_long(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
-+	while((c = getopt_long(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
- #else
- 	app_name = argv[0];
- 
--	while((c = getopt(argc, argv,      "x:d:D:b:i:N:m:g:e:zfqUPhVv")) != EOF) {
-+	while((c = getopt(argc, argv,      "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv")) != EOF) {
- #endif /* HAVE_GETOPT_LONG */
- 		switch(c)
- 		{
-@@ -2510,6 +3262,9 @@
- 			case 'D':
- 				dopt[didx++] = optarg;
- 				break;
-+			case 'B':
-+				blocksize = SI_atof(optarg);
-+				break;
- 			case 'b':
- 				nbblocks = SI_atof(optarg);
- 				break;
-@@ -2519,9 +3274,15 @@
- 			case 'N':
- 				nbinodes = SI_atof(optarg);
- 				break;
-+			case 'L':
-+				volumelabel = optarg;
-+				break;
- 			case 'm':
- 				reserved_frac = SI_atof(optarg) / 100;
- 				break;
-+			case 'o':
-+				creator_os = lookup_creator_os(optarg);
-+				break;
- 			case 'g':
- 				gopt[gidx++] = optarg;
- 				break;
-@@ -2565,21 +3326,21 @@
- 		error_msg_and_die("Not enough arguments. Try --help or else see the man page.");
- 	fsout = argv[optind];
- 
--	hdlinks.hdl = (struct hdlink_s *)malloc(hdlink_cnt * sizeof(struct hdlink_s));
--	if (!hdlinks.hdl)
--		error_msg_and_die("Not enough memory");
--	hdlinks.count = 0 ;
-+	if(blocksize != 1024 && blocksize != 2048 && blocksize != 4096)
-+		error_msg_and_die("Valid block sizes: 1024, 2048 or 4096.");
-+	if(creator_os < 0)
-+		error_msg_and_die("Creator OS unknown.");
- 
- 	if(fsin)
- 	{
- 		if(strcmp(fsin, "-"))
- 		{
- 			FILE * fh = xfopen(fsin, "rb");
--			fs = load_fs(fh, bigendian);
-+			fs = load_fs(fh, bigendian, fsout);
- 			fclose(fh);
- 		}
- 		else
--			fs = load_fs(stdin, bigendian);
-+			fs = load_fs(stdin, bigendian, fsout);
- 	}
- 	else
- 	{
-@@ -2609,16 +3370,29 @@
- 		}
- 		if(fs_timestamp == -1)
- 			fs_timestamp = time(NULL);
--		fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp);
-+		fs = init_fs(nbblocks, nbinodes, nbresrvd, holes,
-+			     fs_timestamp, creator_os, bigendian, fsout);
- 	}
-+	if (volumelabel != NULL)
-+		strncpy((char *)fs->sb->s_volume_name, volumelabel,
-+			sizeof(fs->sb->s_volume_name));
- 	
- 	populate_fs(fs, dopt, didx, squash_uids, squash_perms, fs_timestamp, NULL);
- 
- 	if(emptyval) {
- 		uint32 b;
--		for(b = 1; b < fs->sb.s_blocks_count; b++)
--			if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b),GRP_BBM_OFFSET(fs,b)))
--				memset(get_blk(fs, b), emptyval, BLOCKSIZE);
-+		for(b = 1; b < fs->sb->s_blocks_count; b++) {
-+			blk_info *bi;
-+			gd_info *gi;
-+			if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b,&bi,&gi),
-+				      GRP_BBM_OFFSET(fs,b))) {
-+				blk_info *bi2;
-+				memset(get_blk(fs, b, &bi2), emptyval,
-+				       BLOCKSIZE);
-+				put_blk(bi2);
-+			}
-+			GRP_PUT_BLOCK_BITMAP(bi,gi);
-+		}
- 	}
- 	if(verbose)
- 		print_fs(fs);
-@@ -2628,24 +3402,22 @@
- 		char fname[MAX_FILENAME];
- 		char *p;
- 		FILE *fh;
-+		nod_info *ni;
- 		if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i])))
- 			error_msg_and_die("path %s not found in filesystem", gopt[i]);
- 		while((p = strchr(gopt[i], '/')))
- 			*p = '_';
- 		SNPRINTF(fname, MAX_FILENAME-1, "%s.blk", gopt[i]);
- 		fh = xfopen(fname, "wb");
--		fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
-+		fprintf(fh, "%d:", get_nod(fs, nod, &ni)->i_size);
-+		put_nod(ni);
- 		flist_blocks(fs, nod, fh);
- 		fclose(fh);
- 	}
--	if(strcmp(fsout, "-"))
--	{
--		FILE * fh = xfopen(fsout, "wb");
--		dump_fs(fs, fh, bigendian);
--		fclose(fh);
--	}
--	else
--		dump_fs(fs, stdout, bigendian);
-+	finish_fs(fs);
-+	if(strcmp(fsout, "-") == 0)
-+		copy_file(fs, stdout, fs->f, fs->sb->s_blocks_count);
-+
- 	free_fs(fs);
- 	return 0;
- }
-Index: genext2fs-1.4.1/cache.h
-===================================================================
---- /dev/null
-+++ genext2fs-1.4.1/cache.h
-@@ -0,0 +1,128 @@
-+#ifndef __CACHE_H__
-+#define __CACHE_H__
-+
-+#include "list.h"
-+
-+#define CACHE_LISTS 256
-+
-+typedef struct
-+{
-+    list_elem link;
-+    list_elem lru_link;
-+} cache_link;
-+
-+typedef struct
-+{
-+    /* LRU list holds unused items */
-+    unsigned int lru_entries;
-+    list_elem lru_list;
-+    unsigned int max_free_entries;
-+
-+    unsigned int entries;
-+    list_elem lists[CACHE_LISTS];
-+    unsigned int (*elem_val)(cache_link *elem);
-+    void (*freed)(cache_link *elem);
-+} listcache;
-+
-+static inline void
-+cache_add(listcache *c, cache_link *elem)
-+{
-+    unsigned int hash = c->elem_val(elem) % CACHE_LISTS;
-+    int delcount = c->lru_entries - c->max_free_entries;
-+
-+    if (delcount > 0) {
-+        /* Delete some unused items. */
-+        list_elem *lru, *next;
-+        cache_link *l;
-+        list_for_each_elem_safe(&c->lru_list, lru, next) {
-+            l = container_of(lru, cache_link, lru_link);
-+            list_del(lru);
-+            list_del(&l->link);
-+            c->entries--;
-+            c->lru_entries--;
-+            c->freed(l);
-+            delcount--;
-+            if (delcount <= 0)
-+                break;
-+        }
-+    }
-+
-+    c->entries++;
-+    list_item_init(&elem->lru_link); /* Mark it not in the LRU list */
-+    list_add_after(&c->lists[hash], &elem->link);
-+}
-+
-+static inline void
-+cache_item_set_unused(listcache *c, cache_link *elem)
-+{
-+    list_add_before(&c->lru_list, &elem->lru_link);
-+    c->lru_entries++;
-+}
-+
-+static inline cache_link *
-+cache_find(listcache *c, unsigned int val)
-+{
-+    unsigned int hash = val % CACHE_LISTS;
-+    list_elem *elem;
-+
-+    list_for_each_elem(&c->lists[hash], elem) {
-+        cache_link *l = container_of(elem, cache_link, link);
-+        if (c->elem_val(l) == val) {
-+            if (!list_empty(&l->lru_link)) {
-+                /* It's in the unused list, remove it. */
-+                list_del(&l->lru_link);
-+                list_item_init(&l->lru_link);
-+                c->lru_entries--;
-+            }
-+            return l;
-+        }
-+    }
-+    return NULL;
-+}
-+
-+static inline int
-+cache_flush(listcache *c)
-+{
-+    list_elem *elem, *next;
-+    cache_link *l;
-+    int i;
-+
-+    list_for_each_elem_safe(&c->lru_list, elem, next) {
-+        l = container_of(elem, cache_link, lru_link);
-+        list_del(elem);
-+        list_del(&l->link);
-+        c->entries--;
-+        c->lru_entries--;
-+        c->freed(l);
-+    }
-+
-+    for (i = 0; i < CACHE_LISTS; i++) {
-+        list_for_each_elem_safe(&c->lists[i], elem, next) {
-+            l = container_of(elem, cache_link, link);
-+            list_del(&l->link);
-+            c->entries--;
-+            c->freed(l);
-+        }
-+    }
-+
-+    return c->entries || c->lru_entries;
-+}
-+
-+static inline void
-+cache_init(listcache *c, unsigned int max_free_entries,
-+       unsigned int (*elem_val)(cache_link *elem),
-+       void (*freed)(cache_link *elem))
-+{
-+    int i;
-+
-+    c->entries = 0;
-+    c->lru_entries = 0;
-+    c->max_free_entries = max_free_entries;
-+    list_init(&c->lru_list);
-+    for (i = 0; i < CACHE_LISTS; i++)
-+        list_init(&c->lists[i]);
-+    c->elem_val = elem_val;
-+    c->freed = freed;
-+}
-+
-+#endif /* __CACHE_H__ */
-Index: genext2fs-1.4.1/list.h
-===================================================================
---- /dev/null
-+++ genext2fs-1.4.1/list.h
-@@ -0,0 +1,78 @@
-+#ifndef __LIST_H__
-+#define __LIST_H__
-+
-+#if STDC_HEADERS
-+# include <stdlib.h>
-+# include <stddef.h>
-+#else
-+# if HAVE_STDLIB_H
-+#  include <stdlib.h>
-+# endif
-+# if HAVE_STDDEF_H
-+#  include <stddef.h>
-+# endif
-+#endif
-+
-+#ifndef offsetof
-+#define offsetof(st, m) \
-+     ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
-+#endif
-+
-+#define container_of(ptr, type, member) ({ \
-+                const typeof( ((type *)0)->member ) *__mptr = (ptr); \
-+                (type *)( (char *)__mptr - offsetof(type,member) );})
-+
-+typedef struct list_elem
-+{
-+    struct list_elem *next;
-+    struct list_elem *prev;
-+} list_elem;
-+
-+static inline void list_init(list_elem *list)
-+{
-+    list->next = list;
-+    list->prev = list;
-+}
-+
-+static inline void list_add_after(list_elem *pos, list_elem *elem)
-+{
-+    elem->next = pos->next;
-+    elem->prev = pos;
-+    pos->next->prev = elem;
-+    pos->next = elem;
-+}
-+
-+static inline void list_add_before(list_elem *pos, list_elem *elem)
-+{
-+    elem->prev = pos->prev;
-+    elem->next = pos;
-+    pos->prev->next = elem;
-+    pos->prev = elem;
-+}
-+
-+static inline void list_del(list_elem *elem)
-+{
-+    elem->next->prev = elem->prev;
-+    elem->prev->next = elem->next;
-+}
-+
-+static inline void list_item_init(list_elem *elem)
-+{
-+    elem->next = elem;
-+    elem->prev = elem;
-+}
-+
-+static inline int list_empty(list_elem *elem)
-+{
-+    return elem->next == elem;
-+}
-+
-+#define list_for_each_elem(list, curr)            \
-+    for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next)
-+
-+#define list_for_each_elem_safe(list, curr, next)    \
-+    for ((curr) = (list)->next, (next) = (curr)->next;    \
-+         (curr) != (list);                    \
-+         (curr) = (next), (next) = (curr)->next)
-+
-+#endif /* __LIST_H__ */
diff --git a/package/genext2fs/genext2fs.hash b/package/genext2fs/genext2fs.hash
index add44c4c4e..20bb1c641b 100644
--- a/package/genext2fs/genext2fs.hash
+++ b/package/genext2fs/genext2fs.hash
@@ -1,3 +1,3 @@ 
 # Locally computed:
-sha256  404dbbfa7a86a6c3de8225c8da254d026b17fd288e05cec4df2cc7e1f4feecfc  genext2fs-1.4.1.tar.gz
+sha256  d3861e4fe89131bd21fbd25cf0b683b727b5c030c4c336fadcd738ada830aab0  genext2fs-1.5.0.tar.gz
 sha256  32b1062f7da84967e7019d01ab805935caa7ab7321a7ced0e30ebe75e5df1670  COPYING
diff --git a/package/genext2fs/genext2fs.mk b/package/genext2fs/genext2fs.mk
index dd907c8b12..adfa412e66 100644
--- a/package/genext2fs/genext2fs.mk
+++ b/package/genext2fs/genext2fs.mk
@@ -4,10 +4,12 @@ 
 #
 ################################################################################
 
-GENEXT2FS_VERSION = 1.4.1
-GENEXT2FS_SITE = http://downloads.sourceforge.net/project/genext2fs/genext2fs/$(GENEXT2FS_VERSION)
+GENEXT2FS_VERSION = 1.5.0
+GENEXT2FS_SITE = $(call github,bestouff,genext2fs,v$(GENEXT2FS_VERSION))
 GENEXT2FS_LICENSE = GPL-2.0
 GENEXT2FS_LICENSE_FILES = COPYING
+# From git
+GENEXT2FS_AUTORECONF = YES
 
 $(eval $(autotools-package))
 $(eval $(host-autotools-package))