Hi Vladimir,

On Monday 05 October 2009 15:59, Vladimir Dronnikov wrote:
> Attached is my attempt to bring the subject back to BB (3550 bytes,
> definitely can be shrunk more).
> It is a _very_ stripped down version of genext2fs utility, which is
> able to put given files to an EXT2 filesystem stored as a file.
> Only -m, -L and -F options are of use (the only I ever needed so far),
> little-endian archs. The rationale is to keep mke2fs simplest, moving
> tuning to tune2fs, which has almost identical option set.
> 
> I use it as:
> # mke2fs -m 0 -L LABEL -B1024 fs

You introduce a command line incompatibility right from the start.
Instead of -B<blocks>, it should be a second parameter.

> The resulting "fs" can be cleanly mounted via loop-mount, fed with the
> stuff, then unmounted. This is, of course, much looser test than
> vanilla e2fsck (which I do not have), but it works.

Do you know where to download e2fsprogs, or you prefer I'll do it for you?
Ok.

# dd </dev/zero >zz2 bs=1M count=1
1+0 records in
1+0 records out
# ./busybox mke2fs zz2
# e2fsck -f zz2
e2fsck 1.34 (25-Jul-2003)
Filesystem did not have a UUID; generating one.

Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Directory entry for '.' is big.  Split<y>? 

Whoops.


Just to be sure, standard one:

# dd </dev/zero >zz2 bs=1M count=1
1+0 records in
1+0 records out
# mke2fs zz2
mke2fs 1.34 (25-Jul-2003)
zz2 is not a block special device.
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
128 inodes, 1024 blocks
51 blocks (4.98%) reserved for the super user
First data block=1
1 block group
8192 blocks per group, 8192 fragments per group
128 inodes per group

Writing inode tables: done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 22 mounts or
180000000 days, whichever comes first.  Use tune2fs -c or -i to override.
# e2fsck -f zz2
e2fsck 1.34 (25-Jul-2003)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
zz2: 11/128 files (0.0% non-contiguous), 34/1024 blocks


> Still lacks:
> * big-endian archs
> * auto determination of block count using xstat() call -- so far block
> count must be provided via -B option
> * /lost+found auto creation -- personally I don't need it, one can
> mount the resulting fs, mkdir lost+found, umount -- this saves about
> 2Kb
> * journalling, extended options -O
> 
> Further code cleanup became difficult to me because I do not know well
> the guts of EXT2. In particular, how to retrieve block count from
> block device or file?

xlseek(SEEK_END) / BLOCKSIZE ?

> Why block size is 1024? How to not allocate 
> memory for the whole filesystem? And so on...
> It would be great if you (or someone on the list who talked of
> reviving mke2fs in BB) continue to develop it!

I usually do not rewrite from scratch, I take existing
program and shrink it. If you want to unload this
incomplete work on someone, Rob is probably your guy.
But it would be nice if code is good enough so that
e2fsck does not complain.

Updated patch is attached. If you intend to improve it
so that e2fsck does not complain, please base your work
on this patch.
--
vda
diff -d -urpN busybox.1/coreutils/cal.c busybox.2/coreutils/cal.c
--- busybox.1/coreutils/cal.c	2009-10-06 21:58:22.000000000 +0200
+++ busybox.2/coreutils/cal.c	2009-10-07 01:44:58.000000000 +0200
@@ -147,7 +147,7 @@ int cal_main(int argc, char **argv)
 		unsigned *dp;
 		char lineout[80];
 
-		sprintf(lineout, "%u", year);
+		sprintf(lineout, "%d", year);
 		center(lineout,
 			   (WEEK_LEN * 3 + HEAD_SEP * 2)
 			   + julian * (J_WEEK_LEN * 2 + HEAD_SEP
diff -d -urpN busybox.1/include/applets.h busybox.2/include/applets.h
--- busybox.1/include/applets.h	2009-09-26 15:14:57.000000000 +0200
+++ busybox.2/include/applets.h	2009-10-07 01:46:25.000000000 +0200
@@ -258,9 +258,9 @@ IF_MESG(APPLET(mesg, _BB_DIR_USR_BIN, _B
 IF_MICROCOM(APPLET(microcom, _BB_DIR_USR_BIN, _BB_SUID_DROP))
 IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, _BB_DIR_BIN, _BB_SUID_DROP, mkdir))
 IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat))
-//IF_MKE2FS(APPLET(mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP))
+IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
 IF_MKFIFO(APPLET(mkfifo, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext2, mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
+IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
 //IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext3))
 IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_minix))
 IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat))
diff -d -urpN busybox.1/include/usage.h busybox.2/include/usage.h
--- busybox.1/include/usage.h	2009-10-06 21:07:47.000000000 +0200
+++ busybox.2/include/usage.h	2009-10-07 01:48:50.000000000 +0200
@@ -2704,37 +2704,6 @@
        "/tmp/foo/bar/baz: No such file or directory\n" \
        "$ mkdir -p /tmp/foo/bar/baz\n"
 
-#define mke2fs_trivial_usage \
-       "[-c|-l filename] [-b block-size] [-f fragment-size] [-g blocks-per-group] " \
-       "[-i bytes-per-inode] [-j] [-J journal-options] [-N number-of-inodes] [-n] " \
-       "[-m reserved-blocks-percentage] [-o creator-os] [-O feature[,...]] [-q] " \
-       "[r fs-revision-level] [-E extended-options] [-v] [-F] [-L volume-label] " \
-       "[-M last-mounted-directory] [-S] [-T filesystem-type] " \
-       "device [blocks-count]"
-#define mke2fs_full_usage "\n\n" \
-       "	-b size		Block size in bytes" \
-     "\n	-c		Check for bad blocks before creating" \
-     "\n	-E opts		Set extended options" \
-     "\n	-f size		Fragment size in bytes" \
-     "\n	-F		Force (ignore sanity checks)" \
-     "\n	-g num		Number of blocks in a block group" \
-     "\n	-i ratio	The bytes/inode ratio" \
-     "\n	-j		Create a journal (ext3)" \
-     "\n	-J opts		Set journal options (size/device)" \
-     "\n	-l file		Read bad blocks list from file" \
-     "\n	-L lbl		Set the volume label" \
-     "\n	-m percent	Percent of fs blocks to reserve for admin" \
-     "\n	-M dir		Set last mounted directory" \
-     "\n	-n		Don't actually create anything" \
-     "\n	-N num		Number of inodes to create" \
-     "\n	-o os		Set the 'creator os' field" \
-     "\n	-O features	Dir_index/filetype/has_journal/journal_dev/sparse_super" \
-     "\n	-q		Quiet" \
-     "\n	-r rev		Set filesystem revision" \
-     "\n	-S		Write superblock and group descriptors only" \
-     "\n	-T fs-type	Set usage type (news/largefile/largefile4)" \
-     "\n	-v		Verbose" \
-
 #define mkfifo_trivial_usage \
        "[OPTIONS] name"
 #define mkfifo_full_usage "\n\n" \
@@ -2745,6 +2714,38 @@
      "\n	-Z	Set security context" \
 	)
 
+#define mkfs_ext2_trivial_usage \
+       /* "[-c|-l filename] [-b block-size] [-f fragment-size] [-g blocks-per-group] " */ \
+       /* "[-i bytes-per-inode] [-j] [-J journal-options] [-N number-of-inodes] [-n] " */ \
+       "[-m PERCENT] " /* "[-o creator-os] [-O feature[,...]] [-q] " */ \
+       /* "[r fs-revision-level] [-E extended-options] [-v] [-F] " */ "[-L LBL] " \
+       /* "[-M last-mounted-directory] [-S] [-T filesystem-type] " */ \
+       "device [blocks-count]" \
+
+#define mkfs_ext2_full_usage "\n" \
+    /*   "	-b size		Block size in bytes" */ \
+    /* "\n	-c		Check for bad blocks before creating" */ \
+    /* "\n	-E opts		Set extended options" */ \
+    /* "\n	-f size		Fragment size in bytes" */ \
+    /* "\n	-F		Force (ignore sanity checks)" */ \
+    /* "\n	-g num		Number of blocks in a block group" */ \
+    /* "\n	-i ratio	The bytes/inode ratio" */ \
+    /* "\n	-j		Create a journal (ext3)" */ \
+    /* "\n	-J opts		Set journal options (size/device)" */ \
+    /* "\n	-l file		Read bad blocks list from file" */ \
+     "\n	-L LBL		Set the volume label" \
+     "\n	-m PERCENT	Percent of fs blocks to reserve for admin" \
+    /* "\n	-M dir		Set last mounted directory" */ \
+    /* "\n	-n		Don't actually create anything" */ \
+    /* "\n	-N num		Number of inodes to create" */ \
+    /* "\n	-o os		Set the 'creator os' field" */ \
+    /* "\n	-O features	Dir_index/filetype/has_journal/journal_dev/sparse_super" */ \
+    /* "\n	-q		Quiet" */ \
+    /* "\n	-r rev		Set filesystem revision" */ \
+    /* "\n	-S		Write superblock and group descriptors only" */ \
+    /* "\n	-T fs-type	Set usage type (news/largefile/largefile4)" */ \
+    /* "\n	-v		Verbose" */ \
+
 #define mkfs_minix_trivial_usage \
        "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]"
 #define mkfs_minix_full_usage "\n\n" \
diff -d -urpN busybox.1/util-linux/Config.in busybox.2/util-linux/Config.in
--- busybox.1/util-linux/Config.in	2009-09-26 15:14:57.000000000 +0200
+++ busybox.2/util-linux/Config.in	2009-10-06 23:39:19.000000000 +0200
@@ -239,6 +239,12 @@ config MKFS_VFAT
 	help
 	  Utility to create FAT32 filesystems.
 
+config MKFS_EXT2
+	bool "mkfs_ext2"
+	default n
+	help
+	  Utility to create EXT2 filesystems.
+
 config GETOPT
 	bool "getopt"
 	default n
diff -d -urpN busybox.1/util-linux/Kbuild busybox.2/util-linux/Kbuild
--- busybox.1/util-linux/Kbuild	2009-09-26 15:14:57.000000000 +0200
+++ busybox.2/util-linux/Kbuild	2009-10-06 23:39:19.000000000 +0200
@@ -22,6 +22,7 @@ lib-$(CONFIG_IPCRM)             += ipcrm
 lib-$(CONFIG_IPCS)              += ipcs.o
 lib-$(CONFIG_LOSETUP)           += losetup.o
 lib-$(CONFIG_MDEV)              += mdev.o
+lib-$(CONFIG_MKFS_EXT2)         += mkfs_ext2.o
 lib-$(CONFIG_MKFS_MINIX)        += mkfs_minix.o
 lib-$(CONFIG_MKFS_VFAT)         += mkfs_vfat.o
 lib-$(CONFIG_MKSWAP)            += mkswap.o
diff -d -urpN busybox.1/util-linux/mkfs_ext2.c busybox.2/util-linux/mkfs_ext2.c
--- busybox.1/util-linux/mkfs_ext2.c	1970-01-01 01:00:00.000000000 +0100
+++ busybox.2/util-linux/mkfs_ext2.c	2009-10-07 02:08:24.000000000 +0200
@@ -0,0 +1,969 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_ext2: utility to create EXT2 filesystem
+ * inspired by genext2fs
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <[email protected]>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+#define LINUX_S_IFWHT  0160000	// whiteout: for writable overlays
+
+#define BLOCKSIZE EXT2_MIN_BLOCK_SIZE
+#define BLOCKS_PER_GROUP	8192
+#define INODES_PER_GROUP	8192
+
+// inode block size (why is it != BLOCKSIZE ?!?)
+/* The field i_blocks in the ext2 inode stores the number of data blocks
+   but in terms of 512 bytes. That is what INODE_BLOCKSIZE represents.
+   INOBLK is the number of such blocks in an actual disk block            */
+#define INODE_BLOCKSIZE   512
+#define INOBLK            (BLOCKSIZE / INODE_BLOCKSIZE)
+
+#define EXT2_INIT_BLOCK    0xFFFFFFFF	// just initialized (not really a block address)
+#define WALK_END           0xFFFFFFFE	// end of a block walk
+
+// 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)
+
+// 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)
+
+// 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 )
+
+// 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)
+
+// 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))
+
+// 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))
+
+// 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))))
+
+// 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)
+
+
+/* blockwalker fields:
+   The blockwalker is used to access all the blocks of a file (including
+   the indirection blocks) through repeated calls to walk_bw.
+
+   bpdir -> index into the inode->i_block[]. Indicates level of indirection.
+   bnum -> total number of blocks so far accessed. including indirection
+           blocks.
+   bpind,bpdind,bptind -> index into indirection blocks.
+
+   bpind, bpdind, bptind do *NOT* index into single, double and triple
+   indirect blocks resp. as you might expect from their names. Instead
+   they are in order the 1st, 2nd & 3rd index to be used
+
+   As an example..
+   To access data block number 70000:
+        bpdir: 15 (we are doing triple indirection)
+        bpind: 0 ( index into the triple indirection block)
+        bpdind: 16 ( index into the double indirection block)
+        bptind: 99 ( index into the single indirection block)
+	70000 = 12 + 256 + 256*256 + 16*256 + 100 (indexing starts from zero)
+
+   So, for double indirection bpind will index into the double indirection
+   block and bpdind into the single indirection block. For single indirection
+   only bpind will be used.
+*/
+
+typedef struct
+{
+	uint32_t bnum;
+	uint32_t bpdir;
+	uint32_t bpind;
+	uint32_t bpdind;
+	uint32_t bptind;
+} blockwalker;
+
+// filesystem structure that support groups
+struct filesystem {
+	uint8_t zero[BLOCKSIZE];	// block 0
+	struct ext2_super_block sb;	// superblock
+	struct ext2_group_desc gd[0];	// group descriptors
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+// rounds qty upto a multiple of siz. siz should be a power of 2
+static uint32_t rndup(uint32_t qty, uint32_t siz) ///vda
+{
+	return (qty + (siz - 1)) & ~(siz - 1);
+}
+
+// temporary working block
+static inline void *get_workblk(void)
+{
+	unsigned char* b = xzalloc(BLOCKSIZE);
+	return b;
+}
+static inline void free_workblk(void *b)
+{
+	free(b);
+}
+
+// check if something is allocated in the bitmap
+static uint32_t allocated(uint8_t *b, uint32_t item)
+{
+	return b[(item-1) / 8] & (1 << ((item-1) % 8));
+}
+
+// return a given block from a filesystem
+static void *get_blk(struct filesystem *fs, uint32_t blk)
+{
+	return (uint8_t *)fs + blk * BLOCKSIZE;
+}
+
+// return a given inode from a filesystem
+static inline struct ext2_inode *get_nod(struct filesystem *fs, uint32_t nod)
+{
+	int grp, offset;
+	struct ext2_inode *itab;
+
+	offset = GRP_IBM_OFFSET(fs, nod);
+	grp = GRP_GROUP_OF_INODE(fs, nod);
+	itab = get_blk(fs, fs->gd[grp].bg_inode_table);
+	return itab + offset - 1;
+}
+
+// allocate a given block/inode in the bitmap
+// allocate first free if item == 0
+static uint32_t allocate(uint8_t *b, uint32_t item)
+{
+	if (!item) {
+		int i;
+		uint8_t bits;
+		for (i = 0; i < BLOCKSIZE; i++) {
+			if ((bits = b[i]) != (uint8_t)-1) {
+				int j;
+				for (j = 0; j < 8; j++)
+					if (!(bits & (1 << j)))
+						break;
+				item = i * 8 + j + 1;
+				break;
+			}
+		}
+		if (i == BLOCKSIZE)
+			return 0;
+	}
+	b[(item-1) / 8] |= (1 << ((item-1) % 8));
+	return item;
+}
+
+// deallocate a given block/inode
+static void deallocate(uint8_t *b, uint32_t item)
+{
+	b[(item-1) / 8] &= ~(1 << ((item-1) % 8));
+}
+
+// allocate a block
+static uint32_t alloc_blk(struct filesystem *fs, uint32_t nod)
+{
+	uint32_t bk = 0;
+	uint32_t grp, nbgroups;
+
+	grp = GRP_GROUP_OF_INODE(fs, nod);
+	nbgroups = GRP_NBGROUPS(fs);
+	bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0);
+	if (!bk) {
+		for (grp = 0; grp < nbgroups && !bk; grp++)
+			bk = allocate(get_blk(fs, fs->gd[grp].bg_block_bitmap), 0);
+		grp--;
+	}
+	if (!bk)
+		bb_error_msg_and_die("couldn't allocate a block (no free space)");
+	if (!(fs->gd[grp].bg_free_blocks_count--))
+		bb_error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)", grp);
+	if (!(fs->sb.s_free_blocks_count--))
+		bb_error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
+	return fs->sb.s_blocks_per_group*grp + bk;
+}
+
+// free a block
+static void free_blk(struct filesystem *fs, uint32_t bk)
+{
+	uint32_t 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++;
+}
+
+// initalize a blockwalker (iterator for blocks list)
+static inline void init_bw(blockwalker *bw)
+{
+	bw->bnum = 0;
+	bw->bpdir = EXT2_INIT_BLOCK;
+}
+
+// return next block of inode (WALK_END for end)
+// if *create>0, append a newly allocated block at the end
+// if *create<0, free the block - warning, the metadata blocks contents is
+//				  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_t walk_bw(struct filesystem *fs, uint32_t nod, blockwalker *bw, int32_t *create, uint32_t hole)
+{
+	uint32_t *bkref = NULL;
+	uint32_t *b;
+	int extend = 0, reduce = 0;
+
+	if (create && (*create) < 0)
+		reduce = 1;
+	if (bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK) {
+		if (create && (*create) > 0) {
+			(*create)--;
+			extend = 1;
+		} else
+			return WALK_END;
+	}
+	// first direct block
+	if (bw->bpdir == EXT2_INIT_BLOCK) {
+		bw->bpdir = 0;
+		bkref = &get_nod(fs, nod)->i_block[0];
+		if (extend) // allocate first block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	}
+	// direct block
+	else if (bw->bpdir < EXT2_NDIR_BLOCKS) {
+		bkref = &get_nod(fs, nod)->i_block[++bw->bpdir];
+		if (extend) // allocate block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free block
+			free_blk(fs, *bkref);
+	}
+	// first block in indirect block
+	else if (bw->bpdir == EXT2_NDIR_BLOCKS) {
+		bw->bnum++;
+		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);
+		if (reduce) // free indirect block
+			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		bkref = &b[bw->bpind];
+		if (extend) // allocate first block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	}
+	// block in indirect block
+	else if ((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1)) {
+		bw->bpind++;
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		bkref = &b[bw->bpind];
+		if (extend) // allocate block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free block
+			free_blk(fs, *bkref);
+	}
+	// first block in first indirect block in first double indirect block
+	else if (bw->bpdir == EXT2_IND_BLOCK) {
+		bw->bnum += 2;
+		bw->bpdir = EXT2_DIND_BLOCK;
+		bw->bpind = 0;
+		bw->bpdind = 0;
+		if (extend) // allocate double indirect block
+			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs, nod);
+		if (reduce) // free double indirect block
+			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		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 = get_blk(fs, b[bw->bpind]);
+		bkref = &b[bw->bpdind];
+		if (extend) // allocate first block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	}
+	// block in indirect block in double indirect block
+	else if ((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1)) {
+		bw->bpdind++;
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		b = get_blk(fs, b[bw->bpind]);
+		bkref = &b[bw->bpdind];
+		if (extend) // allocate block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free block
+			free_blk(fs, *bkref);
+	}
+	// first block in indirect block in double indirect block
+	else if ((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1)) {
+		bw->bnum++;
+		bw->bpdind = 0;
+		bw->bpind++;
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		if (extend) // allocate indirect block
+			b[bw->bpind] = alloc_blk(fs, nod);
+		if (reduce) // free indirect block
+			free_blk(fs, b[bw->bpind]);
+		b = get_blk(fs, b[bw->bpind]);
+		bkref = &b[bw->bpdind];
+		if (extend) // allocate first block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	}
+
+	/* Adding support for triple indirection */
+	/* Just starting triple indirection. Allocate the indirection
+	   blocks and the first data block
+	 */
+	else if (bw->bpdir == EXT2_DIND_BLOCK) {
+	  	bw->bnum += 3;
+		bw->bpdir = EXT2_TIND_BLOCK;
+		bw->bpind = 0;
+		bw->bpdind = 0;
+		bw->bptind = 0;
+		if (extend) // allocate triple indirect block
+			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs, nod);
+		if (reduce) // free triple indirect block
+			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		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 = get_blk(fs, b[bw->bpind]);
+		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 = get_blk(fs, b[bw->bpdind]);
+		bkref = &b[bw->bptind];
+		if (extend) // allocate first data block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	}
+	/* Still processing a single indirect block down the indirection
+	   chain.Allocate a data block for it
+	 */
+	else if ((bw->bpdir == EXT2_TIND_BLOCK)
+	      && (bw->bptind < BLOCKSIZE/4 - 1)
+	) {
+		bw->bptind++;
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		b = get_blk(fs, b[bw->bpind]);
+		b = get_blk(fs, b[bw->bpdind]);
+		bkref = &b[bw->bptind];
+		if (extend) // allocate data block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free block
+			free_blk(fs, *bkref);
+	}
+	/* Finished processing a single indirect block. But still in the
+	   same double indirect block. Allocate new single indirect block
+	   for it and a data block
+	 */
+	else if ((bw->bpdir == EXT2_TIND_BLOCK)
+	      && (bw->bpdind < BLOCKSIZE/4 - 1)
+	) {
+		bw->bnum++;
+		bw->bptind = 0;
+		bw->bpdind++;
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		b = get_blk(fs, b[bw->bpind]);
+		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 = get_blk(fs, b[bw->bpdind]);
+		bkref = &b[bw->bptind];
+		if (extend) // allocate first data block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	}
+	/* Finished processing a double indirect block. Allocate the next
+	   double indirect block and the single,data blocks for it
+	 */
+	else if ((bw->bpdir == EXT2_TIND_BLOCK)
+	      && (bw->bpind < BLOCKSIZE/4 - 1)
+	) {
+		bw->bnum += 2;
+		bw->bpdind = 0;
+		bw->bptind = 0;
+		bw->bpind++;
+		b = get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+		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 = get_blk(fs, b[bw->bpind]);
+		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 = get_blk(fs, b[bw->bpdind]);
+		bkref = &b[bw->bptind];
+		if (extend) // allocate first block
+			*bkref = hole ? 0 : alloc_blk(fs, nod);
+		if (reduce) // free first block
+			free_blk(fs, *bkref);
+	} else
+		bb_error_msg_and_die("file too big!");
+	/* End change for walking triple indirection */
+
+	if (*bkref) {
+		bw->bnum++;
+		if (!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref)))
+			bb_error_msg_and_die("[block %d of inode %d is unallocated!]", *bkref, nod);
+	}
+	if (extend)
+		get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
+	return *bkref;
+}
+
+// add blocks to an inode (file/dir/etc...)
+static void extend_blk(struct filesystem *fs, uint32_t nod, uint8_t *b, int amount)
+{
+	int create = amount;
+	blockwalker bw, lbw;
+	uint32_t bk;
+	init_bw(&bw);
+	if (amount < 0) {
+		uint32_t 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)
+			continue;
+		get_nod(fs, nod)->i_blocks += amount * INOBLK;
+	} else {
+		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_t *)(b + BLOCKSIZE * (amount - create)))[i]) {
+						copyb = 1;
+						break;
+					}
+			}
+			bk = walk_bw(fs, nod, &bw, &create, !copyb);
+			if (bk == WALK_END)
+				break;
+			if (copyb)
+				memcpy(get_blk(fs, bk), b + BLOCKSIZE * (amount - create - 1), BLOCKSIZE);
+		}
+	}
+}
+
+#if 0
+// allocate an inode
+static uint32_t alloc_nod(struct filesystem *fs)
+{
+	uint32_t nod, best_group = 0;
+	uint32_t grp, nbgroups, avefreei;
+
+	nbgroups = GRP_NBGROUPS(fs);
+
+	/* Distribute inodes amongst all the blocks                           */
+	/* For every block group with more than average number of free inodes */
+	/* 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;
+	for (grp = 0; grp < nbgroups; grp++) {
+		if (fs->gd[grp].bg_free_inodes_count < avefreei
+		 || fs->gd[grp].bg_free_inodes_count == 0
+		) {
+			continue;
+		}
+		if (!best_group
+		 || fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count
+		) {
+			best_group = grp;
+		}
+	}
+	nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap), 0);
+	if (!nod)
+		bb_error_msg_and_die("couldn't allocate an inode (no free inode)");
+	if (!(fs->gd[best_group].bg_free_inodes_count--))
+		bb_error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
+	if (!(fs->sb.s_free_inodes_count--))
+		bb_error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
+	return fs->sb.s_inodes_per_group * best_group + nod;
+}
+
+// link an entry (inode #) to a directory
+static void add2dir(struct filesystem *fs, uint32_t dnod, uint32_t nod, const char* name)
+{
+	blockwalker bw;
+	uint32_t bk;
+	uint8_t *b;
+	struct ext2_dir_entry_2 *d;
+	int reclen, nlen;
+	struct ext2_inode *node, *pnode;
+
+	pnode = get_nod(fs, dnod);
+	if ((pnode->i_mode & S_IFMT) != S_IFDIR)
+		bb_error_msg_and_die("can't add '%s' to a non-directory", name);
+	if (!*name)
+		bb_error_msg_and_die("can't create an inode with an empty name");
+	if (strchr(name, '/'))
+		bb_error_msg_and_die("bad name '%s' (contains a slash)", name);
+	nlen = strlen(name);
+	reclen = sizeof(struct ext2_dir_entry_2) + rndup(nlen, 4);
+	if (reclen > BLOCKSIZE)
+		bb_error_msg_and_die("bad name '%s' (too long)", name);
+	init_bw(&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 = (struct ext2_dir_entry_2 *)b;
+			(int8_t *)d + sizeof(*d) < (int8_t *)b + BLOCKSIZE;
+			d = (struct ext2_dir_entry_2 *)((int8_t *)d + d->rec_len)
+		) {
+			// if empty dir entry, large enough, use it
+			if ((!d->inode) && (d->rec_len >= reclen)) {
+				d->inode = nod;
+				node = get_nod(fs, nod);
+				node->i_links_count++;
+				d->name_len = nlen;
+				strncpy(d->name, name, nlen);
+				return;
+			}
+			// if entry with enough room (last one?), shrink it & use it
+			if (d->rec_len >= (sizeof(struct ext2_dir_entry_2) + rndup(d->name_len, 4) + reclen)) {
+				reclen = d->rec_len;
+				d->rec_len = sizeof(struct ext2_dir_entry_2) + rndup(d->name_len, 4);
+				reclen -= d->rec_len;
+				d = (struct ext2_dir_entry_2 *) (((int8_t *)d) + d->rec_len);
+				d->rec_len = reclen;
+				d->inode = nod;
+				node = get_nod(fs, nod);
+				node->i_links_count++;
+				d->name_len = nlen;
+				strncpy(d->name, name, nlen);
+				return;
+			}
+		}
+	}
+	// we found no free entry in the directory, so we add a block
+	b = get_workblk();
+	d = (struct ext2_dir_entry_2 *)b;
+	d->inode = nod;
+	node = get_nod(fs, nod);
+	node->i_links_count++;
+	d->rec_len = BLOCKSIZE;
+	d->name_len = nlen;
+	strncpy(d->name, name, nlen);
+	extend_blk(fs, dnod, b, 1);
+	get_nod(fs, dnod)->i_size += BLOCKSIZE;
+	free_workblk(b);
+}
+
+// find an entry in a directory
+static uint32_t find_dir(struct filesystem *fs, uint32_t nod, const char *name)
+{
+	blockwalker bw;
+	uint32_t bk;
+	int nlen = strlen(name);
+	init_bw(&bw);
+	while ((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END) {
+		struct ext2_dir_entry_2 *d;
+		uint8_t *b;
+		b = get_blk(fs, bk);
+		for (d = (struct ext2_dir_entry_2 *)b;
+			(int8_t *)d + sizeof(*d) < (int8_t *)b + BLOCKSIZE;
+			d = (struct ext2_dir_entry_2 *)((int8_t *)d + d->rec_len)
+		) {
+			if (d->inode && (nlen == d->name_len) && !strncmp(d->name, name, nlen))
+				return d->inode;
+		}
+	}
+	return 0;
+}
+
+// create a simple inode
+static uint32_t mknod_fs(
+	struct filesystem *fs,
+	uint32_t parent_nod,
+	const char *name,
+	uint16_t mode, uint16_t uid, uint16_t gid,
+	uint8_t major,
+	uint8_t minor,
+	uint32_t createtime,
+	uint32_t mtime)
+{
+	uint32_t nod;
+	struct ext2_inode *node;
+	if ((nod = find_dir(fs, parent_nod, name))) {
+		node = get_nod(fs, nod);
+		if ((node->i_mode & S_IFMT) != (mode & S_IFMT))
+			bb_error_msg_and_die("node '%s' already exists and isn't of the same type", name);
+		node->i_mode = mode;
+	} else {
+		nod = alloc_nod(fs);
+		node = get_nod(fs, nod);
+		node->i_mode = mode;
+		add2dir(fs, parent_nod, nod, name);
+		switch (mode & S_IFMT) {
+			case S_IFLNK:
+				mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+				break;
+			case S_IFBLK:
+			case S_IFCHR:
+				((uint8_t *)get_nod(fs, nod)->i_block)[0] = minor;
+				((uint8_t *)get_nod(fs, nod)->i_block)[1] = major;
+				break;
+			case S_IFDIR:
+				add2dir(fs, nod, nod, ".");
+				add2dir(fs, nod, parent_nod, "..");
+				fs->gd[GRP_GROUP_OF_INODE(fs, nod)].bg_used_dirs_count++;
+				break;
+		}
+	}
+	node->i_uid = uid;
+	node->i_gid = gid;
+	node->i_atime = mtime;
+	node->i_ctime = createtime;
+	node->i_mtime = mtime;
+	return nod;
+}
+
+// make a full-fledged directory (i.e. with "." & "..")
+static inline uint32_t mkdir_fs(
+	struct filesystem *fs,
+	uint32_t parent_nod,
+	const char *name,
+	uint32_t mode, uid_t uid, gid_t gid,
+	uint32_t createtime,
+	uint32_t mtime)
+{
+	return mknod_fs(fs, parent_nod, name, mode|S_IFDIR, uid, gid, 0, 0, createtime, mtime);
+}
+#endif
+
+/*
+static void populate_fs(struct filesystem *fs, char **dopt, int didx,
+			int squash_uids, int squash_perms, uint32 fs_timestamp,
+			struct stats *stats)
+{
+	int i;
+	for (i = 0; i < didx; i++) {
+		struct stat st;
+		int pdir;
+		char *pdest;
+		uint32 nod = EXT2_ROOT_INO;
+		if (fs) {
+			if ((pdest = strchr(dopt[i], ':')) != NULL) {
+				*(pdest++) = 0;
+				if (!(nod = find_path(fs, EXT2_ROOT_INO, pdest)))
+					bb_error_msg_and_die("path %s not found in filesystem", pdest);
+			}
+		}
+		stat(dopt[i], &st);
+		switch (st.st_mode & S_IFMT) {
+			case S_IFREG:
+				FILE *fp = xfopen_for_read(dopt[i]);
+				add2fs_from_file(fs, nod, fp, fs_timestamp, stats);
+				fclose(fp);
+				break;
+			case S_IFDIR:
+				if ((pdir = open(".", O_RDONLY)) < 0)
+					bb_perror_msg_and_die(".");
+				xchdir(dopt[i]);
+				add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
+				if (fchdir(pdir) < 0)
+					bb_perror_msg_and_die("fchdir");
+				if (close(pdir) < 0)
+					bb_perror_msg_and_die("close");
+				break;
+			default:
+				bb_error_msg_and_die("%s is neither a file nor a directory", dopt[i]);
+		}
+	}
+}
+*/
+
+static struct filesystem *init_fs(int nblocks, int ninodes, int nreserved, uint32_t fs_timestamp)
+{
+	uint32_t i;
+	struct filesystem *fs;
+	struct ext2_dir_entry_2 *d;
+	uint8_t *b;
+	//uint32_t nod;
+	uint32_t first_block;
+	uint32_t ngroups, ninodes_per_group, overhead_per_group, free_blocks,
+		free_blocks_per_group, nblocks_per_group, min_ngroups;
+	uint32_t gdsz, itblsz, bbmpos, ibmpos, itblpos;
+	uint32_t j;
+	uint8_t *bbm, *ibm;
+	struct ext2_inode *itab0;
+
+	/* ninodes is the total number of inodes in the system.
+	 * a block group can have no more than 8192 inodes.
+	 */
+	min_ngroups = (ninodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
+	/* nblocks is the total number of blocks in the filesystem.
+	 * a block group can have no more than 8192 blocks.
+	 */
+	first_block = 1;
+	ngroups = (nblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP;
+	if (ngroups < min_ngroups)
+		ngroups = min_ngroups;
+
+	nblocks_per_group = rndup((nblocks - first_block + ngroups - 1) / ngroups, 8);
+	ninodes_per_group = rndup((ninodes + ngroups - 1) / ngroups, BLOCKSIZE / sizeof(struct ext2_inode));
+	if (ninodes_per_group < 16)
+		ninodes_per_group = 16; // minimum number since the first 10 are reserved
+
+	gdsz = rndup(ngroups * sizeof(struct ext2_group_desc), BLOCKSIZE) / BLOCKSIZE;
+	itblsz = ninodes_per_group * sizeof(struct ext2_inode) / BLOCKSIZE;
+	overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz;
+	if ((uint32_t)nblocks - 1 < overhead_per_group * ngroups)
+		bb_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 = nblocks - overhead_per_group*ngroups - 1 /*boot block*/;
+	free_blocks_per_group = nblocks_per_group - overhead_per_group;
+
+	fs = xzalloc(nblocks * BLOCKSIZE);
+
+	// create the superblock for an empty filesystem
+	fs->sb.s_inodes_count = ninodes_per_group * ngroups;
+	fs->sb.s_blocks_count = nblocks;
+	fs->sb.s_r_blocks_count = nreserved;
+	fs->sb.s_free_blocks_count = free_blocks;
+	fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_GOOD_OLD_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 = nblocks_per_group;
+	fs->sb.s_frags_per_group = nblocks_per_group;
+	fs->sb.s_inodes_per_group = ninodes_per_group;
+	fs->sb.s_wtime = fs_timestamp;
+	fs->sb.s_magic = EXT2_SUPER_MAGIC;
+	fs->sb.s_lastcheck = fs_timestamp;
+
+	// set up group descriptors
+	for (i = 0, bbmpos = gdsz+2, ibmpos = bbmpos+1, itblpos = ibmpos+1;
+		i < ngroups;
+		i++, bbmpos += nblocks_per_group, ibmpos += nblocks_per_group, itblpos += nblocks_per_group
+	) {
+		if (free_blocks > free_blocks_per_group) {
+			fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
+			free_blocks -= free_blocks_per_group;
+		} else {
+			fs->gd[i].bg_free_blocks_count = free_blocks;
+			free_blocks = 0; // this is the last block group
+		}
+		fs->gd[i].bg_free_inodes_count = ninodes_per_group - (i ? 0 : (EXT2_GOOD_OLD_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;
+	}
+
+	// mark non-filesystem blocks and inodes as allocated
+	// mark system blocks and inodes as allocated
+	for (i = 0; i < ngroups; i++) {
+		// block bitmap
+		bbm = get_blk(fs, fs->gd[i].bg_block_bitmap);
+		// non-filesystem blocks
+		for (j = fs->gd[i].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);
+		// inode bitmap
+		ibm = get_blk(fs, fs->gd[i].bg_inode_bitmap);
+		// non-filesystem inodes
+		for (j = fs->sb.s_inodes_per_group + 1; j <= BLOCKSIZE * 8; j++)
+			allocate(ibm, j);
+		// system inodes
+		if (!i) {
+			for (j = 1; j < EXT2_GOOD_OLD_FIRST_INO; j++)
+				allocate(ibm, j);
+		}
+	}
+
+	// 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 = get_blk(fs, fs->gd[0].bg_inode_table);
+	itab0[EXT2_ROOT_INO-1].i_mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_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;
+
+	b = get_workblk();
+	d = (struct ext2_dir_entry_2 *)b;
+	d->inode = EXT2_ROOT_INO;
+	d->rec_len = sizeof(struct ext2_dir_entry_2) + 4;
+	d->name_len = 1;
+	strcpy(d->name, ".");
+	d = (struct ext2_dir_entry_2 *)(b + d->rec_len);
+	d->inode = EXT2_ROOT_INO;
+	d->rec_len = BLOCKSIZE - (sizeof(struct ext2_dir_entry_2) + 4);
+	d->name_len = 2;
+	strcpy(d->name, "..");
+	extend_blk(fs, EXT2_ROOT_INO, b, 1);
+
+#if 0
+	// make lost+found directory and reserve blocks
+	if (fs->sb.s_r_blocks_count) {
+		nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", S_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
+		memset(b, 0, BLOCKSIZE);
+		((struct ext2_dir_entry_2 *)b)->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;
+	}
+#endif
+	free_workblk(b);
+
+	// administrative info
+	fs->sb.s_state = 1;
+	fs->sb.s_max_mnt_count = 20;
+
+	return fs;
+}
+
+/* Standard mke2fs 1.41.1:
+ * Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size]
+ * 	[-i bytes-per-inode] [-I inode-size] [-J journal-options]
+ * 	[-G meta group size] [-N number-of-inodes]
+ * 	[-m reserved-blocks-percentage] [-o creator-os]
+ * 	[-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
+ * 	[-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
+ * 	[-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count]
+*/
+int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned nblocks, ninodes, nreserved = 5;
+	int ts = time(NULL);
+	unsigned opts;
+	const char *label;
+	struct filesystem *fs;
+	//struct stat st;
+	int fd;
+
+	// NB: we support only options listed below. All the other should go to tune2fs
+	enum {
+		OPT_m = 1 << 0,	 // percent of reserved blocks
+		OPT_L = 1 << 1,	 // volume label
+		// accepted and ignored: -F force, -q quiet, -v verbose
+	};
+
+	opt_complementary = "-1:m+";
+	opts = getopt32(argv, "m:L:Fqv", &nreserved, &label);
+	argv += optind; // argv[0] -- device
+	fd = NOT_LONE_DASH(argv[0]) ? xopen3(argv[0], O_WRONLY | O_CREAT, 0666) : STDOUT_FILENO;
+	if (argv[1]) {
+		nblocks = xatou(argv[1]);
+	} else {
+    		nblocks = ((uoff_t)xlseek(fd, 0, SEEK_END)) / BLOCKSIZE;
+		xlseek(fd, 0, SEEK_SET);
+	}
+
+	// get device parameters
+/*	xstat(argv[0], &st);
+	// check the device is a block device
+	if (!(S_ISBLK(st.st_mode) || (opts & OPT_F)))
+		bb_error_msg_and_die("I refuse to format non-block device");
+	bb_info_msg("NAM[%s]SIZ[%ju]BLK[%ju]INO[%ju]BS[%lu]",
+		argv[0],
+	       (uintmax_t)st.st_size,
+	       (uintmax_t)st.st_blocks,
+	       (uintmax_t)st.st_ino,
+	       (unsigned long)st.st_blksize
+	);
+	return 0;
+*/
+
+	// sanity checks
+	if (nblocks < 8)
+		bb_error_msg_and_die("too few blocks. Note: options have changed, see --help or the man page.");
+
+	// init filesystem
+/*	if (fsin) {
+		size_t fssize;
+
+		fp = xfopen_stdin(fsin);
+		if ((fseek(fp, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fp)) == -1))
+			bb_perror_msg_and_die("input filesystem image");
+		rewind(fp);
+		fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
+		if (fssize < 16) // totally arbitrary
+			bb_error_msg_and_die("too small filesystem");
+		fs = xzalloc(fssize * BLOCKSIZE);
+		if (fread(fs, BLOCKSIZE, fssize, fp) != fssize)
+			bb_perror_msg_and_die("input filesystem image");
+		//if (BB_BIG_ENDIAN)
+		//	swap_badfs(fs);
+		if (fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_SUPER_MAGIC))
+			bb_error_msg_and_die("not a suitable ext2 filesystem");
+		fclose(fp);
+	} else
+*/
+	{
+		nreserved *= nblocks / 100;
+		ninodes = EXT2_GOOD_OLD_FIRST_INO - 1 + (nreserved ? 1 : 0);
+		fs = init_fs(nblocks, ninodes, nreserved, ts);
+	}
+
+	if (opts & OPT_L)
+		safe_strncpy((char *)fs->sb.s_volume_name, label, sizeof(fs->sb.s_volume_name));
+
+	// dump the result
+//	if (BB_BIG_ENDIAN)
+//		swap_goodfs(fs);
+	xwrite(fd, fs, BLOCKSIZE * nblocks);
+//	if (BB_BIG_ENDIAN)
+//		swap_badfs(fs);
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(fs);
+		close(fd);
+	}
+
+	return EXIT_SUCCESS;
+}
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to