summaryrefslogtreecommitdiff
path: root/recipes/genext2fs
diff options
context:
space:
mode:
authorDenys Dmytriyenko <denis@denix.org>2009-03-17 14:32:59 -0400
committerDenys Dmytriyenko <denis@denix.org>2009-03-17 14:32:59 -0400
commit709c4d66e0b107ca606941b988bad717c0b45d9b (patch)
tree37ee08b1eb308f3b2b6426d5793545c38396b838 /recipes/genext2fs
parentfa6cd5a3b993f16c27de4ff82b42684516d433ba (diff)
rename packages/ to recipes/ per earlier agreement
See links below for more details: http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326 http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816 Signed-off-by: Denys Dmytriyenko <denis@denix.org> Acked-by: Mike Westerhof <mwester@dls.net> Acked-by: Philip Balister <philip@balister.org> Acked-by: Khem Raj <raj.khem@gmail.com> Acked-by: Marcin Juszkiewicz <hrw@openembedded.org> Acked-by: Koen Kooi <koen@openembedded.org> Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'recipes/genext2fs')
-rw-r--r--recipes/genext2fs/files/volume.patch107
-rw-r--r--recipes/genext2fs/genext2fs-1.3+cvs/bytes_per_inode.patch76
-rw-r--r--recipes/genext2fs/genext2fs-1.3/autosize.patch339
-rw-r--r--recipes/genext2fs/genext2fs-1.3/misc.patch2143
-rw-r--r--recipes/genext2fs/genext2fs-native_1.3+1.4rc1.bb10
-rw-r--r--recipes/genext2fs/genext2fs-native_1.3+cvs.bb10
-rw-r--r--recipes/genext2fs/genext2fs-native_1.3.bb11
-rw-r--r--recipes/genext2fs/genext2fs.inc4
-rw-r--r--recipes/genext2fs/genext2fs_1.3+1.4rc1.bb10
-rw-r--r--recipes/genext2fs/genext2fs_1.3+cvs.bb9
-rw-r--r--recipes/genext2fs/genext2fs_1.3.bb15
11 files changed, 2734 insertions, 0 deletions
diff --git a/recipes/genext2fs/files/volume.patch b/recipes/genext2fs/files/volume.patch
new file mode 100644
index 0000000000..32ef8dc52b
--- /dev/null
+++ b/recipes/genext2fs/files/volume.patch
@@ -0,0 +1,107 @@
+Index: genext2fs-1.4rc1/genext2fs.c
+===================================================================
+--- genext2fs-1.4rc1.orig/genext2fs.c
++++ genext2fs-1.4rc1/genext2fs.c
+@@ -409,7 +409,9 @@ swab32(uint32 val)
+ typedef struct
+ {
+ superblock_decl
+- uint32 s_reserved[235]; // Reserved
++ uint32 s_reserved1[9];
++ char s_volume[16];
++ uint32 s_reserved2[222]; // Reserved
+ } superblock;
+
+ typedef struct
+@@ -1122,7 +1124,7 @@ extend_blk(filesystem *fs, uint32 nod, b
+ while(create)
+ {
+ int i, copyb = 0;
+- if(!(fs->sb.s_reserved[200] & OP_HOLES))
++ if(!(fs->sb.s_reserved2[187] & OP_HOLES))
+ copyb = 1;
+ else
+ for(i = 0; i < BLOCKSIZE / 4; i++)
+@@ -1784,7 +1786,8 @@ swap_badfs(filesystem *fs)
+
+ // 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, char *volumeid)
+ {
+ int i;
+ filesystem *fs;
+@@ -1847,6 +1850,9 @@ init_fs(int nbblocks, int nbinodes, int
+ fs->sb.s_magic = EXT2_MAGIC_NUMBER;
+ fs->sb.s_lastcheck = fs_timestamp;
+
++ if (volumeid != NULL)
++ strncpy(fs->sb.s_volume, volumeid, sizeof(fs->sb.s_volume));
++
+ // set up groupdescriptors
+ for(i = 0,bbmpos=2+gd,ibmpos=3+gd,itblpos =4+gd;
+ i<nbgroups;
+@@ -1945,7 +1951,7 @@ init_fs(int nbblocks, int nbinodes, int
+
+ // options for me
+ if(holes)
+- fs->sb.s_reserved[200] |= OP_HOLES;
++ fs->sb.s_reserved2[187] |= OP_HOLES;
+
+ return fs;
+ }
+@@ -2276,7 +2282,7 @@ static void
+ dump_fs(filesystem *fs, FILE * fh, int swapit)
+ {
+ int nbblocks = fs->sb.s_blocks_count;
+- fs->sb.s_reserved[200] = 0;
++ fs->sb.s_reserved2[187] = 0;
+ if(swapit)
+ swap_goodfs(fs);
+ if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
+@@ -2341,6 +2347,7 @@ main(int argc, char **argv)
+ uint16 endian = 1;
+ int bigendian = !*(char*)&endian;
+ filesystem *fs;
++ char *volumeid = NULL;
+ int i;
+ int c;
+ struct stats stats;
+@@ -2359,6 +2366,7 @@ main(int argc, char **argv)
+ { "size-in-blocks", required_argument, NULL, 'b' },
+ { "bytes-per-inode", required_argument, NULL, 'i' },
+ { "number-of-inodes", required_argument, NULL, 'I' },
++ { "volume-id", required_argument, NULL, 'L' },
+ { "reserved-blocks", required_argument, NULL, 'r' },
+ { "block-map", required_argument, NULL, 'g' },
+ { "fill-value", required_argument, NULL, 'e' },
+@@ -2372,7 +2380,7 @@ main(int argc, char **argv)
+ { 0, 0, 0, 0}
+ } ;
+
+- while((c = getopt_long(argc, argv, "x:d:D:b:I:i:r:g:e:zfqUPhv", longopts, NULL)) != EOF) {
++ while((c = getopt_long(argc, argv, "x:d:D:b:I:i:L:r:g:e:zfqUPhv", longopts, NULL)) != EOF) {
+ switch(c)
+ {
+ case 'x':
+@@ -2391,6 +2399,9 @@ main(int argc, char **argv)
+ case 'I':
+ nbinodes = SI_atof(optarg);
+ break;
++ case 'L':
++ volumeid = optarg;
++ break;
+ case 'r':
+ nbresrvd = SI_atof(optarg);
+ break;
+@@ -2517,7 +2528,8 @@ main(int argc, char **argv)
+ nbresrvd = nbblocks * RESERVED_BLOCKS;
+ 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, volumeid);
+ }
+ for(i = 0; i < didx; i++)
+ {
diff --git a/recipes/genext2fs/genext2fs-1.3+cvs/bytes_per_inode.patch b/recipes/genext2fs/genext2fs-1.3+cvs/bytes_per_inode.patch
new file mode 100644
index 0000000000..729f5d7708
--- /dev/null
+++ b/recipes/genext2fs/genext2fs-1.3+cvs/bytes_per_inode.patch
@@ -0,0 +1,76 @@
+Index: genext2fs/genext2fs.c
+===================================================================
+--- genext2fs.orig/genext2fs.c 2005-03-10 21:18:08.000000000 -0500
++++ genext2fs/genext2fs.c 2005-03-13 11:41:55.182746832 -0500
+@@ -2284,6 +2284,7 @@
+ " -d, --root <directory>\n"
+ " -D, --devtable <file>\n"
+ " -b, --size-in-blocks <blocks>\n"
++ " -I, --bytes-per-inode <bytes per inode>\n"
+ " -i, --number-of-inodes <number of inodes>\n"
+ " -r, --reserved-blocks <number of reserved blocks>\n"
+ " -g, --block-map <path> Generate a block map file for this path.\n"
+@@ -2314,6 +2315,7 @@
+ int nbresrvd = -1;
+ int tmp_nbblocks = -1;
+ int tmp_nbinodes = -1;
++ int bytes_per_inode = BYTES_PER_INODE;
+ uint32 fs_timestamp = -1;
+ char * fsout = "-";
+ char * fsin = 0;
+@@ -2345,6 +2347,7 @@
+ { "root", required_argument, NULL, 'd' },
+ { "devtable", required_argument, NULL, 'D' },
+ { "size-in-blocks", required_argument, NULL, 'b' },
++ { "bytes-per-inode", required_argument, NULL, 'I' },
+ { "number-of-inodes", required_argument, NULL, 'i' },
+ { "reserved-blocks", required_argument, NULL, 'r' },
+ { "block-map", required_argument, NULL, 'g' },
+@@ -2359,7 +2362,7 @@
+ { 0, 0, 0, 0}
+ } ;
+
+- while((c = getopt_long(argc, argv, "x:d:D:b:i:r:g:e:zfqUPhv", longopts, NULL)) != EOF) {
++ while((c = getopt_long(argc, argv, "x:d:D:b:I:i:r:g:e:zfqUPhv", longopts, NULL)) != EOF) {
+ switch(c)
+ {
+ case 'x':
+@@ -2372,6 +2375,9 @@
+ case 'b':
+ nbblocks = atoi(optarg);
+ break;
++ case 'I':
++ bytes_per_inode = atoi(optarg);
++ break;
+ case 'i':
+ nbinodes = atoi(optarg);
+ break;
+@@ -2468,24 +2474,22 @@
+ if(pdest)
+ *pdest = ':';
+ }
+-
++
+ tmp_nbinodes = stats.ninodes + EXT2_FIRST_INO + 1;
+ tmp_nbblocks = stats.nblocks; // FIXME: should add space taken by inodes too
+-
++
+ if(tmp_nbblocks > nbblocks)
+ {
+ fprintf(stderr, "number of blocks too low, increasing to %d\n",tmp_nbblocks);
+ nbblocks = tmp_nbblocks;
+ }
++ if(nbinodes == -1)
++ nbinodes = nbblocks * BLOCKSIZE / rndup(bytes_per_inode, BLOCKSIZE);
+ if(tmp_nbinodes > nbinodes)
+ {
+ fprintf(stderr, "number of inodes too low, increasing to %d\n",tmp_nbinodes);
+ nbinodes = tmp_nbinodes;
+ }
+- if(nbblocks == -1)
+- error_msg_and_die("filesystem size unspecified");
+- if(nbinodes == -1)
+- nbinodes = nbblocks * BLOCKSIZE / rndup(BYTES_PER_INODE, BLOCKSIZE);
+ if(nbresrvd == -1)
+ nbresrvd = nbblocks * RESERVED_BLOCKS;
+ if(fs_timestamp == -1)
diff --git a/recipes/genext2fs/genext2fs-1.3/autosize.patch b/recipes/genext2fs/genext2fs-1.3/autosize.patch
new file mode 100644
index 0000000000..a4318a6eee
--- /dev/null
+++ b/recipes/genext2fs/genext2fs-1.3/autosize.patch
@@ -0,0 +1,339 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- genext2fs-1.3.orig/genext2fs.c~autosize.patch
++++ genext2fs-1.3.orig/genext2fs.c
+@@ -4,6 +4,11 @@
+ // ext2 filesystem generator for embedded systems
+ // Copyright (C) 2000 Xavier Bestel <xavier.bestel@free.fr>
+ //
++// 'du' portions taken from coreutils/du.c in busybox:
++// Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
++// Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
++// Copyright (C) 2002 Edward Betts <edward@debian.org>
++//
+ // This program is free software; you can redistribute it and/or
+ // modify it under the terms of the GNU General Public License
+ // as published by the Free Software Foundation; version
+@@ -79,9 +84,93 @@
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
++#include <sys/types.h>
++#include <getopt.h>
++
++#define HASH_SIZE 311 /* Should be prime */
++#define hash_inode(i) ((i) % HASH_SIZE)
++
++typedef struct ino_dev_hash_bucket_struct {
++ struct ino_dev_hash_bucket_struct *next;
++ ino_t ino;
++ dev_t dev;
++ char name[1];
++} ino_dev_hashtable_bucket_t;
++
++static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
++
++struct stats {
++ unsigned long nblocks;
++ unsigned long ninodes;
++};
++
++int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name)
++{
++ ino_dev_hashtable_bucket_t *bucket;
++
++ bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
++ while (bucket != NULL) {
++ if ((bucket->ino == statbuf->st_ino) &&
++ (bucket->dev == statbuf->st_dev))
++ {
++ if (name) *name = bucket->name;
++ return 1;
++ }
++ bucket = bucket->next;
++ }
++ return 0;
++}
++
++/* Add statbuf to statbuf hash table */
++void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
++{
++ int i;
++ size_t s;
++ ino_dev_hashtable_bucket_t *bucket;
++
++ i = hash_inode(statbuf->st_ino);
++ s = name ? strlen(name) : 0;
++ bucket = malloc(sizeof(ino_dev_hashtable_bucket_t) + s);
++ bucket->ino = statbuf->st_ino;
++ bucket->dev = statbuf->st_dev;
++ if (name)
++ strcpy(bucket->name, name);
++ else
++ bucket->name[0] = '\0';
++ bucket->next = ino_dev_hashtable[i];
++ ino_dev_hashtable[i] = bucket;
++}
++
++/* Clear statbuf hash table */
++void reset_ino_dev_hashtable(void)
++{
++ int i;
++ ino_dev_hashtable_bucket_t *bucket;
++
++ for (i = 0; i < HASH_SIZE; i++) {
++ while (ino_dev_hashtable[i] != NULL) {
++ bucket = ino_dev_hashtable[i]->next;
++ free(ino_dev_hashtable[i]);
++ ino_dev_hashtable[i] = bucket;
++ }
++ }
++}
+
++static int count_ino_in_hashtable(void)
++{
++ long count = 0;
++ int i;
+
++ for (i = 0; i < HASH_SIZE; i++) {
++ ino_dev_hashtable_bucket_t *bucket = ino_dev_hashtable[i];
++ while (bucket != NULL) {
++ count++;
++ bucket = bucket->next;
++ }
++ }
+
++ return count;
++}
+
+ // block size
+
+@@ -1178,6 +1267,38 @@
+ return n;
+ }
+
++void stats_from_dir(struct stats *stats)
++{
++ DIR *dh;
++ struct dirent *dent;
++ struct stat st;
++ if(!(dh = opendir(".")))
++ perror_msg_and_die(".");
++ while((dent = readdir(dh)))
++ {
++ if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
++ continue;
++ lstat(dent->d_name, &st);
++ if (S_ISLNK(st.st_mode)) {
++ stats->ninodes++;
++ } else if (S_ISDIR(st.st_mode)) {
++ if(chdir(dent->d_name) < 0)
++ perror_msg_and_die(dent->d_name);
++ stats->ninodes++;
++ stats_from_dir(stats);
++ chdir("..");
++ } else {
++ if (!is_in_ino_dev_hashtable(&st, NULL)) {
++ add_to_ino_dev_hashtable(&st, NULL);
++ stats->nblocks += (st.st_blocks >> 1);
++ stats->ninodes++;
++ }
++ }
++ }
++ closedir(dh);
++ reset_ino_dev_hashtable();
++}
++
+ // adds a tree of entries to the filesystem from current dir
+ void add2fs_from_dir(filesystem *fs, uint32 this_nod)
+ {
+@@ -1436,7 +1557,6 @@
+ free_blocks_per_group = nbblocks_per_group - overhead_per_group;
+ }
+ nbblocks = nbblocks_per_group * nbgroups + 1;
+-
+
+ if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
+ error_msg_and_die("not enough memory for filesystem");
+@@ -1891,6 +2011,7 @@
+ Regular files must exist in the target root directory. If a char,
+ block, fifo, or directory does not exist, it will be created.
+ */
++
+ static int interpret_table_entry(filesystem *fs, char *line)
+ {
+ char type, *name = NULL, *tmp, *dir, *bname;
+@@ -2026,6 +2147,52 @@
+ return 0;
+ }
+
++static int stats_from_table_entry(char *line, struct stats *stats)
++{
++ char type, *name = NULL, *tmp, *dir, *bname;
++ unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
++ unsigned long start = 0, increment = 1, count = 0;
++ inode *entry;
++
++ if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
++ SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor,
++ &start, &increment, &count) < 0)
++ {
++ return 1;
++ }
++
++ if (!strcmp(name, "/")) {
++ error_msg_and_die("Device table entries require absolute paths");
++ }
++
++ tmp = xstrdup(name);
++ bname = xstrdup(basename(tmp));
++ free(tmp);
++ switch (type) {
++ case 'd':
++ stats->ninodes++;
++ break;
++ case 'c':
++ case 'b':
++ if (count > 0) {
++ dev_t rdev;
++ char *dname;
++ unsigned long i;
++ for (i = start; i < count; i++) {
++ asprintf(&dname, "%s%lu", bname, i);
++ stats->ninodes++;
++ free(dname);
++ }
++ } else {
++ stats->ninodes++;
++ }
++ break;
++ }
++ free(bname);
++ free(name);
++ return 0;
++}
++
+ static int parse_device_table(filesystem *root, FILE * file)
+ {
+ char *line;
+@@ -2070,6 +2237,45 @@
+ return status;
+ }
+
++static int stats_from_dev_table(FILE *file, struct stats *stats)
++{
++ char *line;
++ int status = 0;
++ size_t length = 0;
++
++ /* Looks ok so far. The general plan now is to read in one
++ * line at a time, check for leading comment delimiters ('#'),
++ * then try and parse the line as a device table. If we fail
++ * to parse things, try and help the poor fool to fix their
++ * device table with a useful error msg... */
++ line = NULL;
++ while (getline(&line, &length, file) != -1) {
++ /* First trim off any whitespace */
++ int len = strlen(line);
++
++ /* trim trailing whitespace */
++ while (len > 0 && isspace(line[len - 1]))
++ line[--len] = '\0';
++ /* trim leading whitespace */
++ memmove(line, &line[strspn(line, " \n\r\t\v")], len);
++
++ /* How long are we after trimming? */
++ len = strlen(line);
++
++ /* If this is NOT a comment line, try to interpret it */
++ if (len && *line != '#') {
++ if (stats_from_table_entry(line, stats))
++ status = 1;
++ }
++
++ free(line);
++ line = NULL;
++ }
++ fclose(file);
++
++ return status;
++}
++
+ /*
+ Local Variables:
+ c-file-style: "linux"
+@@ -2112,6 +2318,8 @@
+ int nbblocks = -1;
+ int nbinodes = -1;
+ int nbresrvd = -1;
++ int tmp_nbblocks = -1;
++ int tmp_nbinodes = -1;
+ char * fsout = "-";
+ char * fsin = 0;
+ char * dopt[MAX_DOPT];
+@@ -2128,6 +2336,7 @@
+ int c;
+ struct stat sb;
+ FILE *devtable = NULL;
++ struct stats stats;
+
+ app_name = argv[0];
+ while((c = getopt(argc, argv, "x:d:b:i:r:g:e:zvhD:f:qUP")) != EOF)
+@@ -2184,6 +2393,7 @@
+ default:
+ exit(1);
+ }
++
+ if(optind < (argc - 1))
+ error_msg_and_die("too many arguments");
+ if(optind == (argc - 1))
+@@ -2201,6 +2411,46 @@
+ }
+ else
+ {
++ stats.ninodes = 0;
++ stats.nblocks = 0;
++ for(i = 0; i < didx; i++)
++ {
++ struct stat st;
++ char *pdir;
++ stat(dopt[i], &st);
++ switch(st.st_mode & S_IFMT)
++ {
++ case S_IFDIR:
++ if(!(pdir = getcwd(0, GETCWD_SIZE)))
++ perror_msg_and_die(dopt[i]);
++ if(chdir(dopt[i]) < 0)
++ perror_msg_and_die(dopt[i]);
++ stats_from_dir(&stats);
++ if(chdir(pdir) < 0)
++ perror_msg_and_die(pdir);
++ free(pdir);
++ break;
++ default:
++ error_msg_and_die("%s is neither a file nor a directory", dopt[i]);
++ }
++ }
++
++ if(devtable)
++ stats_from_dev_table(devtable, &stats);
++
++ tmp_nbinodes = stats.ninodes + EXT2_FIRST_INO + 1;
++ tmp_nbblocks = stats.nblocks;
++
++ if(tmp_nbblocks > nbblocks)
++ {
++ printf("Number of blocks too low, increasing to %d\n",tmp_nbblocks);
++ nbblocks = tmp_nbblocks;
++ }
++ if(tmp_nbinodes > nbinodes)
++ {
++ printf("Number of inodes too low, increasing to %d\n",tmp_nbinodes);
++ nbinodes = tmp_nbinodes;
++ }
+ if(nbblocks == -1)
+ error_msg_and_die("filesystem size unspecified");
+ if(nbinodes == -1)
diff --git a/recipes/genext2fs/genext2fs-1.3/misc.patch b/recipes/genext2fs/genext2fs-1.3/misc.patch
new file mode 100644
index 0000000000..e5849eb797
--- /dev/null
+++ b/recipes/genext2fs/genext2fs-1.3/misc.patch
@@ -0,0 +1,2143 @@
+--- genext2fs-1.3.orig/Makefile
++++ genext2fs-1.3/Makefile
+@@ -0,0 +1,25 @@
++all: genext2fs
++INSTALL=install
++
++install:
++ $(INSTALL) -d $(DESTDIR)/usr/bin/
++ $(INSTALL) -m 755 genext2fs $(DESTDIR)/usr/bin/
++ $(INSTALL) -d $(DESTDIR)/usr/share/man/man8/
++ $(INSTALL) -m 644 genext2fs.8 $(DESTDIR)/usr/share/man/man8/
++
++clean:
++ -rm genext2fs
++ rm -rf test ext2.img
++
++check: all
++ mkdir -p test
++ dd if=/dev/zero of=test/zero count=1
++ ./genext2fs -b 4096 -d test ext2.img
++
++ md5=`md5sum ext2.img | cut -f 1 -d " "`; \
++ if [ "$$md5" != "a736fce6d45ea3631b01fd7b8f623131" ] ; then \
++ echo "test failed."; \
++ else \
++ echo "test succeeded."; \
++ fi
++
+diff -urN genext2fs-1.3.orig/dev.txt genext2fs-1.3/dev.txt
+--- genext2fs-1.3.orig/dev.txt 2000-09-28 09:03:19.000000000 -0600
++++ genext2fs-1.3/dev.txt 1969-12-31 17:00:00.000000000 -0700
+@@ -1,94 +0,0 @@
+-drwx /dev
+-crw- 10,190 /dev/lcd
+-crw- 10,191 /dev/splc781
+-crw- 4,0 /dev/console
+-crw- 5,64 /dev/cua0
+-crw- 5,65 /dev/cua1
+-crw- 5,66 /dev/cua2
+-crw- 5,70 /dev/cua6
+-crw- 5,71 /dev/cua7
+-crw- 5,72 /dev/cua8
+-crw- 5,73 /dev/cua9
+-crw- 29,0 /dev/fb0
+-crw- 29,32 /dev/fb1
+-crw- 1,2 /dev/kmem
+-crw- 1,1 /dev/mem
+-crw- 1,3 /dev/null
+-crw- 2,2 /dev/ptyp2
+-crw- 2,3 /dev/ptyp3
+-crw- 2,5 /dev/ptyp5
+-crw- 2,4 /dev/ptyp4
+-crw- 10,178 /dev/triokb
+-crw- 2,0 /dev/ptyp0
+-crw- 2,6 /dev/ptyp6
+-crw- 2,7 /dev/ptyp7
+-crw- 2,8 /dev/ptyp8
+-crw- 2,9 /dev/ptyp9
+-crw- 2,10 /dev/ptypa
+-crw- 2,11 /dev/ptypb
+-crw- 2,12 /dev/ptypc
+-crw- 2,13 /dev/ptypd
+-crw- 2,14 /dev/ptype
+-crw- 2,15 /dev/ptypf
+-brw- 1,0 /dev/ram0
+-brw- 1,1 /dev/ram1
+-brw- 1,2 /dev/ram2
+-brw- 1,3 /dev/ram3
+-br-- 31,0 /dev/rom0
+-brw- 31,1 /dev/rom1
+-brw- 31,2 /dev/rom2
+-brw- 31,3 /dev/rom3
+-crw- 5,0 /dev/tty
+-crw- 4,0 /dev/tty0
+-crwx 4,1 /dev/tty1
+-crwx 4,2 /dev/tty2
+-crwx 4,3 /dev/tty3
+-crwx 4,4 /dev/tty4
+-crw- 4,5 /dev/tty5
+-crwx 4,6 /dev/tty6
+-crw- 4,7 /dev/tty7
+-crw- 4,8 /dev/tty8
+-crw- 4,9 /dev/tty9
+-crw- 4,64 /dev/ttyS0
+-crw- 4,65 /dev/ttyS1
+-crw- 4,66 /dev/ttyS2
+-crw- 4,67 /dev/ttyS3
+-crw- 4,68 /dev/ttyS4
+-crw- 4,69 /dev/ttyS5
+-crw- 4,70 /dev/ttyS6
+-crw- 4,71 /dev/ttyS7
+-crw- 4,72 /dev/ttyS8
+-crw- 4,73 /dev/ttyS9
+-crw- 3,0 /dev/ttyp0
+-crw- 3,1 /dev/ttyp1
+-crw- 3,2 /dev/ttyp2
+-crw- 3,3 /dev/ttyp3
+-crw- 3,4 /dev/ttyp4
+-crw- 3,5 /dev/ttyp5
+-crw- 3,6 /dev/ttyp6
+-crw- 3,7 /dev/ttyp7
+-crw- 3,8 /dev/ttyp8
+-crw- 3,9 /dev/ttyp9
+-crw- 3,10 /dev/ttypa
+-crw- 3,11 /dev/ttypb
+-crw- 3,12 /dev/ttypc
+-crw- 3,13 /dev/ttypd
+-crw- 3,14 /dev/ttype
+-crw- 3,15 /dev/ttypf
+-crw- 1,5 /dev/zero
+-crwx 10,111 /dev/dtedrv
+-crwx 4,110 /dev/ttyM
+-crw- 77,1 /dev/tssnd
+-crw- 77,2 /dev/tstone
+-crw- 2,1 /dev/ptyp1
+-crwx 10,180 /dev/triohook
+-crw- 90,0 /dev/mtd0
+-brw- 44,0 /dev/ftl0
+-crw- 10,175 /dev/tporta
+-crw- 10,176 /dev/tportb
+-crwx 10,100 /dev/softmodem
+-crwx 10,101 /dev/softmodem_signals
+-crwx 10,181 /dev/triovoice
+-crw- 5,67 /dev/cua3
+-crw- 5,68 /dev/cua4
+-crw- 5,69 /dev/cua5
+diff -urN genext2fs-1.3.orig/device_table.txt genext2fs-1.3/device_table.txt
+--- genext2fs-1.3.orig/device_table.txt 1969-12-31 17:00:00.000000000 -0700
++++ genext2fs-1.3/device_table.txt 2003-04-21 01:41:42.000000000 -0600
+@@ -0,0 +1,129 @@
++# When building a target filesystem, it is desirable to not have to
++# become root and then run 'mknod' a thousand times. Using a device
++# table you can create device nodes and directories "on the fly".
++#
++# This is a sample device table file for use with genext2fs. You can
++# do all sorts of interesting things with a device table file. For
++# example, if you want to adjust the permissions on a particular file
++# you can just add an entry like:
++# /sbin/foobar f 2755 0 0 - - - - -
++# and (assuming the file /sbin/foobar exists) it will be made setuid
++# root (regardless of what its permissions are on the host filesystem.
++# Furthermore, you can use a single table entry to create a many device
++# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15]
++# I could just use the following two table entries:
++# /dev/hda b 640 0 0 3 0 0 0 -
++# /dev/hda b 640 0 0 3 1 1 1 15
++#
++# Device table entries take the form of:
++# <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
++# where name is the file name, type can be one of:
++# f A regular file
++# d Directory
++# c Character special device file
++# b Block special device file
++# p Fifo (named pipe)
++# uid is the user id for the target file, gid is the group id for the
++# target file. The rest of the entries (major, minor, etc) apply only
++# to device special files.
++
++# Have fun
++# -Erik Andersen <andersen@codepoet.org>
++#
++
++#<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
++/dev d 755 0 0 - - - - -
++/dev/mem c 640 0 0 1 1 0 0 -
++/dev/kmem c 640 0 0 1 2 0 0 -
++/dev/null c 640 0 0 1 3 0 0 -
++/dev/zero c 640 0 0 1 5 0 0 -
++/dev/random c 640 0 0 1 8 0 0 -
++/dev/urandom c 640 0 0 1 9 0 0 -
++/dev/tty c 666 0 0 5 0 0 0 -
++/dev/tty c 666 0 0 4 0 0 1 6
++/dev/console c 640 0 0 5 1 0 0 -
++/dev/ram b 640 0 0 1 1 0 0 -
++/dev/ram b 640 0 0 1 0 0 1 4
++/dev/loop b 640 0 0 7 0 0 1 2
++/dev/ptmx c 666 0 0 5 2 0 0 -
++#/dev/ttyS c 640 0 0 4 64 0 1 4
++#/dev/psaux c 640 0 0 10 1 0 0 -
++#/dev/rtc c 640 0 0 10 135 0 0 -
++
++# Adjust permissions on some normal files
++#/etc/shadow f 600 0 0 - - - - -
++#/bin/tinylogin f 4755 0 0 - - - - -
++
++# User-mode Linux stuff
++/dev/ubda b 640 0 0 98 0 0 0 -
++/dev/ubda b 640 0 0 98 1 1 1 15
++
++# IDE Devices
++/dev/hda b 640 0 0 3 0 0 0 -
++/dev/hda b 640 0 0 3 1 1 1 15
++/dev/hdb b 640 0 0 3 64 0 0 -
++/dev/hdb b 640 0 0 3 65 1 1 15
++#/dev/hdc b 640 0 0 22 0 0 0 -
++#/dev/hdc b 640 0 0 22 1 1 1 15
++#/dev/hdd b 640 0 0 22 64 0 0 -
++#/dev/hdd b 640 0 0 22 65 1 1 15
++#/dev/hde b 640 0 0 33 0 0 0 -
++#/dev/hde b 640 0 0 33 1 1 1 15
++#/dev/hdf b 640 0 0 33 64 0 0 -
++#/dev/hdf b 640 0 0 33 65 1 1 15
++#/dev/hdg b 640 0 0 34 0 0 0 -
++#/dev/hdg b 640 0 0 34 1 1 1 15
++#/dev/hdh b 640 0 0 34 64 0 0 -
++#/dev/hdh b 640 0 0 34 65 1 1 15
++
++# SCSI Devices
++#/dev/sda b 640 0 0 8 0 0 0 -
++#/dev/sda b 640 0 0 8 1 1 1 15
++#/dev/sdb b 640 0 0 8 16 0 0 -
++#/dev/sdb b 640 0 0 8 17 1 1 15
++#/dev/sdc b 640 0 0 8 32 0 0 -
++#/dev/sdc b 640 0 0 8 33 1 1 15
++#/dev/sdd b 640 0 0 8 48 0 0 -
++#/dev/sdd b 640 0 0 8 49 1 1 15
++#/dev/sde b 640 0 0 8 64 0 0 -
++#/dev/sde b 640 0 0 8 65 1 1 15
++#/dev/sdf b 640 0 0 8 80 0 0 -
++#/dev/sdf b 640 0 0 8 81 1 1 15
++#/dev/sdg b 640 0 0 8 96 0 0 -
++#/dev/sdg b 640 0 0 8 97 1 1 15
++#/dev/sdh b 640 0 0 8 112 0 0 -
++#/dev/sdh b 640 0 0 8 113 1 1 15
++#/dev/sg c 640 0 0 21 0 0 1 15
++#/dev/scd b 640 0 0 11 0 0 1 15
++#/dev/st c 640 0 0 9 0 0 1 8
++#/dev/nst c 640 0 0 9 128 0 1 8
++#/dev/st c 640 0 0 9 32 1 1 4
++#/dev/st c 640 0 0 9 64 1 1 4
++#/dev/st c 640 0 0 9 96 1 1 4
++
++# Floppy disk devices
++#/dev/fd b 640 0 0 2 0 0 1 2
++#/dev/fd0d360 b 640 0 0 2 4 0 0 -
++#/dev/fd1d360 b 640 0 0 2 5 0 0 -
++#/dev/fd0h1200 b 640 0 0 2 8 0 0 -
++#/dev/fd1h1200 b 640 0 0 2 9 0 0 -
++#/dev/fd0u1440 b 640 0 0 2 28 0 0 -
++#/dev/fd1u1440 b 640 0 0 2 29 0 0 -
++#/dev/fd0u2880 b 640 0 0 2 32 0 0 -
++#/dev/fd1u2880 b 640 0 0 2 33 0 0 -
++
++# All the proprietary cdrom devices in the world
++#/dev/aztcd b 640 0 0 29 0 0 0 -
++#/dev/bpcd b 640 0 0 41 0 0 0 -
++#/dev/capi20 c 640 0 0 68 0 0 1 2
++#/dev/cdu31a b 640 0 0 15 0 0 0 -
++#/dev/cdu535 b 640 0 0 24 0 0 0 -
++#/dev/cm206cd b 640 0 0 32 0 0 0 -
++#/dev/sjcd b 640 0 0 18 0 0 0 -
++#/dev/sonycd b 640 0 0 15 0 0 0 -
++#/dev/gscd b 640 0 0 16 0 0 0 -
++#/dev/sbpcd b 640 0 0 25 0 0 0 -
++#/dev/sbpcd b 640 0 0 25 0 0 1 4
++#/dev/mcd b 640 0 0 23 0 0 0 -
++#/dev/optcd b 640 0 0 17 0 0 0 -
++
+diff -urN genext2fs-1.3.orig/genext2fs.8 genext2fs-1.3/genext2fs.8
+--- genext2fs-1.3.orig/genext2fs.8 1969-12-31 17:00:00.000000000 -0700
++++ genext2fs-1.3/genext2fs.8 2003-04-21 01:41:42.000000000 -0600
+@@ -0,0 +1,125 @@
++.\" Hey, EMACS: -*- nroff -*-
++.\" First parameter, NAME, should be all caps
++.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
++.\" other parameters are allowed: see man(7), man(1)
++.TH GENEXT2FS 8 "July 14, 2001"
++.\" Please adjust this date whenever revising the manpage.
++.\"
++.\" Some roff macros, for reference:
++.\" .nh disable hyphenation
++.\" .hy enable hyphenation
++.\" .ad l left justify
++.\" .ad b justify to both left and right margins
++.\" .nf disable filling
++.\" .fi enable filling
++.\" .br insert line break
++.\" .sp <n> insert n+1 empty lines
++.\" for manpage-specific macros, see man(7)
++.SH NAME
++genext2fs \- ext2 filesystem generator for embedded systems
++.SH SYNOPSIS
++.B genext2fs
++.RI [ options ] " image"
++.SH DESCRIPTION
++\fBgenext2fs\fP generates an ext2 filesystem
++as a normal (non-root) user. It doesn't require you to mount
++the image file to copy files on it. It doesn't even require
++you to be the superuser to make device nodes.
++.SH OPTIONS
++.TP
++.BI -x \ image
++Use this image as a starting point
++.TP
++.BI -d \ directory
++Add this directory as source
++.TP
++.BI -f \ FILE
++.TP
++.BI -D \ FILE
++Uses the named FILE as a device table file, to create device
++nodes and directories "on the fly".
++.TP
++.BI -b \ blocks
++Size in blocks
++.TP
++.BI -i \ inodes
++Number of inodes
++.TP
++.BI -r \ reserved
++Number of reserved blocks
++.TP
++.BI -g \ path
++Generate a block map file for this path
++.TP
++.BI -e \ value
++Fill unallocated blocks with value
++.TP
++.BI -z
++Make files with holes
++.TP
++.BI -U
++Squash owners making all files be owned by root
++.TP
++.BI -P
++Squash permissions on all files
++.TP
++.BI -q
++Squash permissions and owners (same as -P -U)
++.TP
++.BI -v
++Print resulting filesystem structure
++.TP
++.BI -h
++Display help
++.TP
++.SH EXAMPLES
++
++.EX
++.B
++ genext2fs -b 1440 -d src /dev/fd0
++.EE
++
++All files in the
++.I src
++directory will be written to
++.B /dev/fd0
++as a new ext2 filesystem image. You can then mount the floppy as
++usual.
++
++.EX
++.B
++ genext2fs -b 1024 -d src -D device_table.txt flashdisk.img
++.EE
++
++This example builds a filesystem from all the files in
++.I src
++, then device nodes are created based on the content the device_table file
++.I dev.txt.
++An example device file follows:
++
++.EX
++ #<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
++ /dev d 755 0 0 - - - - -
++ /dev/mem c 640 0 0 1 1 0 0 -
++ /dev/tty c 666 0 0 5 0 0 0 -
++ /dev/tty c 666 0 0 4 0 0 1 6
++ /dev/loop b 640 0 0 7 0 0 1 2
++ /dev/hda b 640 0 0 3 0 0 0 -
++ /dev/hda b 640 0 0 3 1 1 1 16
++.EE
++
++This device table creates the /dev directory, a character device
++node /dev/mem (major 1, minor 1), it also creates /dev/tty,
++/dev/tty[0-5], /dev/loop[0-1], /dev/hda, and /dev/hda0 to /dev/hda15
++.SH BUGS
++\fBgenext2fs\fP does not support hard links. Hard links present in the input
++tree will be represented as separate files in the ext2 image.
++
++.SH SEE ALSO
++.BR mkfs (8),
++.BR genromfs (8),
++.BR mkisofs (8).
++.br
++.SH AUTHOR
++This manual page was written by David Kimdon <dwhedon@debian.org>,
++for the Debian GNU/Linux system (but may be used by others).
+diff -urN genext2fs-1.3.orig/genext2fs.c genext2fs-1.3/genext2fs.c
+--- genext2fs-1.3.orig/genext2fs.c 2001-06-18 02:11:32.000000000 -0600
++++ genext2fs-1.3/genext2fs.c 2003-04-21 01:48:35.000000000 -0600
+@@ -1,3 +1,4 @@
++/* vi: set sw=8 ts=8: */
+ // genext2fs.c
+ //
+ // ext2 filesystem generator for embedded systems
+@@ -26,6 +27,22 @@
+ // Bugfix: getcwd values for Solaris xavier.gueguen@col.bsf.alcatel.fr
+ // Bugfix: ANSI scanf for non-GNU C xavier.gueguen@col.bsf.alcatel.fr
+ // 28 Jun 2001 Bugfix: getcwd differs for Solaris/GNU mike@sowbug.com
++// 23 Mar 2002 Bugfix: test for IFCHR or IFBLK was flawed
++// 10 Oct 2002 Added comments,makefile targets, vsundar@ixiacom.com
++// endianess swap assert check.
++// Copyright (C) 2002 Ixia communications
++// 12 Oct 2002 Added support for triple indirection vsundar@ixiacom.com
++// Copyright (C) 2002 Ixia communications
++// 14 Oct 2002 Added support for groups vsundar@ixiacom.com
++// Copyright (C) 2002 Ixia communications
++// 5 Jan 2003 Bugfixes: reserved inodes should be set vsundar@usc.edu
++// only in the first group; directory names
++// need to be null padded at the end; and
++// number of blocks per group should be a
++// multiple of 8. Updated md5 values.
++// 6 Jan 2003 Erik Andersen <andersee@debian.org> added
++// mkfs.jffs2 compatible device table support,
++// along with -q, -P, -U
+
+
+ // `genext2fs' is a mean to generate an ext2 filesystem
+@@ -33,10 +50,6 @@
+ // the image file to copy files on it. It doesn't even require
+ // you to be the superuser to make device nodes.
+ //
+-// Warning ! `genext2fs' has been designed for embedded
+-// systems. As such, it will generate a filesystem for single-user
+-// usage: all files/directories/etc... will belong to UID/GID 0
+-//
+ // Example usage:
+ //
+ // # genext2fs -b 1440 -d srcdir /dev/fd0
+@@ -45,21 +58,15 @@
+ // a new ext2 filesystem image. You can then mount the floppy as
+ // usual.
+ //
+-// # genext2fs -b 1024 -d builddir -f devices.txt flashdisk.img
++// # genext2fs -b 1024 -d builddir -D device_table.txt flashdisk.img
+ //
+ // This one would build a filesystem from all the files in builddir,
+-// then would read a devices list and make apropriate nodes. The
+-// format for the device list is:
+-//
+-// drwx /dev
+-// crw- 10,190 /dev/lcd
+-// brw- 1,0 /dev/ram0
+-//
+-// This device list builds the /dev directory, a character device
+-// node /dev/lcd (major 10, minor 190) and a block device node
+-// /dev/ram0 (major 1, minor 0)
++// then would read the device_table.txt file and make apropriate nodes.
++// The format for the device table file is covered in detail in the sample
++// device_table.txt file provided with the genext2fs source.
+
+
++#define _GNU_SOURCE
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -67,6 +74,11 @@
+ #include <stdarg.h>
+ #include <unistd.h>
+ #include <sys/stat.h>
++#include <assert.h>
++#include <time.h>
++#include <ctype.h>
++#include <errno.h>
++#include <fcntl.h>
+
+
+
+@@ -76,10 +88,14 @@
+ #define BLOCKSIZE 1024
+ #define BLOCKS_PER_GROUP 8192
+ #define BYTES_PER_INODE (8*BLOCKSIZE)
++/* Percentage of blocks that are reserved.*/
+ #define RESERVED_INODES 5/100
+
+
+ // 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)
+@@ -147,6 +163,39 @@
+
+ #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-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) )
++
++// 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 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 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))) )
++
++//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 the group it belongs to
++#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 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 )
++
+
+ // used types
+
+@@ -287,7 +336,6 @@
+ {
+ groupdescriptor_decl
+ uint32 bg_reserved[3];
+- uint32 bg_pad_to_bk[(BLOCKSIZE-32)/sizeof(uint32)];
+ } groupdescriptor;
+
+ typedef struct
+@@ -304,6 +352,32 @@
+
+ typedef uint8 block[BLOCKSIZE];
+
++/* 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 bnum;
+@@ -313,15 +387,14 @@
+ uint32 bptind;
+ } blockwalker;
+
++
++/* Filesystem structure that support groups */
+ #if BLOCKSIZE == 1024
+ typedef struct
+ {
+ block zero; // The famous block 0
+ superblock sb; // The superblock
+- groupdescriptor gd; // The group desciptor
+- block bbm; // The block bitmap
+- block ibm; // The inode bitmap
+- inode itab[0]; // The inode table
++ groupdescriptor gd[0]; // The group descriptors
+ } filesystem;
+ #else
+ #error UNHANDLED BLOCKSIZE
+@@ -389,25 +462,113 @@
+ #undef udecl32
+ #undef utdecl32
+
+-char * argv0;
++static char * app_name;
++static int squash_uids = 0;
++static int squash_perms = 0;
++static const char *const memory_exhausted = "memory exhausted";
+
+ // error (un)handling
+-inline void errexit(const char *fmt, ...)
++static void verror_msg(const char *s, va_list p)
+ {
+- va_list ap;
+- fprintf(stderr, "%s: ", argv0);
+- va_start(ap, fmt);
+- vfprintf(stderr, fmt, ap);
+- va_end(ap);
+- fprintf(stderr, "\n");
+- exit(1);
++ fflush(stdout);
++ fprintf(stderr, "%s: ", app_name);
++ vfprintf(stderr, s, p);
++}
++static void error_msg(const char *s, ...)
++{
++ va_list p;
++ va_start(p, s);
++ verror_msg(s, p);
++ va_end(p);
++ putc('\n', stderr);
++}
++
++static void error_msg_and_die(const char *s, ...)
++{
++ va_list p;
++ va_start(p, s);
++ verror_msg(s, p);
++ va_end(p);
++ putc('\n', stderr);
++ exit(EXIT_FAILURE);
++}
++
++static void vperror_msg(const char *s, va_list p)
++{
++ int err = errno;
++ if (s == 0)
++ s = "";
++ verror_msg(s, p);
++ if (*s)
++ s = ": ";
++ fprintf(stderr, "%s%s\n", s, strerror(err));
++}
++
++#if 0
++static void perror_msg(const char *s, ...)
++{
++ va_list p;
++ va_start(p, s);
++ vperror_msg(s, p);
++ va_end(p);
++}
++#endif
++static void perror_msg_and_die(const char *s, ...)
++{
++ va_list p;
++ va_start(p, s);
++ vperror_msg(s, p);
++ va_end(p);
++ exit(EXIT_FAILURE);
+ }
+
+-inline void pexit(const char * fname)
++static FILE *xfopen(const char *path, const char *mode)
+ {
+- fprintf(stderr, "%s: ", argv0);
+- perror(fname);
+- exit(1);
++ FILE *fp;
++ if ((fp = fopen(path, mode)) == NULL)
++ perror_msg_and_die("%s", path);
++ return fp;
++}
++
++static char *xstrdup(const char *s)
++{
++ char *t;
++
++ if (s == NULL)
++ return NULL;
++ t = strdup(s);
++ if (t == NULL)
++ error_msg_and_die(memory_exhausted);
++ return t;
++}
++
++extern void *xrealloc(void *ptr, size_t size)
++{
++ ptr = realloc(ptr, size);
++ if (ptr == NULL && size != 0)
++ error_msg_and_die(memory_exhausted);
++ return ptr;
++}
++
++static char *xreadlink(const char *path)
++{
++ static const int GROWBY = 80; /* how large we will grow strings by */
++
++ char *buf = NULL;
++ int bufsize = 0, readsize = 0;
++
++ do {
++ buf = xrealloc(buf, bufsize += GROWBY);
++ readsize = readlink(path, buf, bufsize); /* 1st try */
++ if (readsize == -1) {
++ perror_msg_and_die("%s:%s", app_name, path);
++ }
++ }
++ while (bufsize < readsize + 1);
++
++ buf[readsize] = '\0';
++
++ return buf;
+ }
+
+ // printf helper macro
+@@ -423,7 +584,7 @@
+ {
+ }
+
+-// rounds a quantity up to a blocksize
++/* Rounds qty upto a multiple of siz. siz should be a power of 2 */
+ uint32 rndup(uint32 qty, uint32 siz)
+ {
+ return (qty + (siz - 1)) & ~(siz - 1);
+@@ -444,7 +605,13 @@
+ // return a given inode from a filesystem
+ inline inode * get_nod(filesystem *fs, uint32 nod)
+ {
+- return &fs->itab[nod-1];
++ int grp,offset;
++ inode *itab;
++
++ offset = GRP_IBM_OFFSET(fs,nod);
++ grp = GRP_GROUP_OF_INODE(fs,nod);
++ itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table);
++ return itab+offset-1;
+ }
+
+ // allocate a given block/inode in the bitmap
+@@ -479,29 +646,57 @@
+ }
+
+ // allocate a block
+-uint32 alloc_blk(filesystem *fs)
++uint32 alloc_blk(filesystem *fs, uint32 nod)
+ {
+- uint32 bk;
+- if(!(bk = allocate(fs->bbm, 0)))
+- errexit("couldn't allocate a block (no free space)");
+- if(!(fs->gd.bg_free_blocks_count--))
+- errexit("group descr. free blocks count == 0 (corrupted fs?)");
++ uint32 bk=0;
++ uint32 grp,nbgroups;
++
++ grp = nod/fs->sb.s_inodes_per_group;
++ nbgroups = ( fs->sb.s_blocks_count - fs->sb.s_first_data_block + fs->sb.s_blocks_per_group -1 ) /
++ fs->sb.s_blocks_per_group;
++ 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);
++ grp--;
++ }
++ if (!bk)
++ error_msg_and_die("couldn't allocate a block (no free space)");
++ if(!(fs->gd[grp].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--))
+- errexit("superblock free blocks count == 0 (corrupted fs?)");
+- return bk;
++ error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
++ return fs->sb.s_blocks_per_group*grp + bk;
+ }
+
+ // allocate an inode
+ uint32 alloc_nod(filesystem *fs)
+ {
+- uint32 nod;
+- if(!(nod = allocate(fs->ibm, 0)))
+- errexit("couldn't allocate an inode (no free inode)");
+- if(!(fs->gd.bg_free_inodes_count--))
+- errexit("group descr. free blocks count == 0 (corrupted fs?)");
++ uint32 nod=0,best_group=0;
++ uint32 grp,nbgroups,avefreei;
++
++ nbgroups = ( fs->sb.s_blocks_count - fs->sb.s_first_data_block + fs->sb.s_blocks_per_group -1 ) /
++ fs->sb.s_blocks_per_group;
++
++ /* 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 && !nod;grp++) {
++ if (fs->gd[grp].bg_free_inodes_count < avefreei)
++ continue;
++ if (!best_group ||
++ fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count)
++ best_group = grp;
++ }
++ if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0)))
++ error_msg_and_die("couldn't allocate an inode (no free inode)");
++ if(!(fs->gd[best_group].bg_free_inodes_count--))
++ error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
+ if(!(fs->sb.s_free_inodes_count--))
+- errexit("superblock free blocks count == 0 (corrupted fs?)");
+- return nod;
++ error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
++ return fs->sb.s_inodes_per_group*best_group+nod;
+ }
+
+ // print a bitmap allocation
+@@ -546,14 +741,14 @@
+ {
+ bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0];
+ if(extend) // allocate first block
+- *bkref = hole ? 0 : alloc_blk(fs);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+ // 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);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+ // first block in indirect block
+ else if(bw->bpdir == EXT2_NDIR_BLOCKS)
+@@ -562,11 +757,11 @@
+ bw->bpdir = EXT2_IND_BLOCK;
+ bw->bpind = 0;
+ if(extend) // allocate indirect block
+- get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs);
++ get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
+ b = (uint32*)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);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+ // block in indirect block
+ else if((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
+@@ -575,7 +770,7 @@
+ b = (uint32*)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);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+ // first block in first indirect block in first double indirect block
+ else if(bw->bpdir == EXT2_IND_BLOCK)
+@@ -585,14 +780,14 @@
+ bw->bpind = 0;
+ bw->bpdind = 0;
+ if(extend) // allocate double indirect block
+- get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs);
++ get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ if(extend) // allocate first indirect block
+- b[bw->bpind] = alloc_blk(fs);
++ b[bw->bpind] = alloc_blk(fs,nod);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ bkref = &b[bw->bpdind];
+ if(extend) // allocate first block
+- *bkref = hole ? 0 : alloc_blk(fs);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+ // block in indirect block in double indirect block
+ else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1))
+@@ -602,7 +797,7 @@
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ bkref = &b[bw->bpdind];
+ if(extend) // allocate block
+- *bkref = hole ? 0 : alloc_blk(fs);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+ // first block in indirect block in double indirect block
+ else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
+@@ -612,20 +807,100 @@
+ bw->bpind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ if(extend) // allocate indirect block
+- b[bw->bpind] = alloc_blk(fs);
++ b[bw->bpind] = alloc_blk(fs,nod);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ bkref = &b[bw->bpdind];
+ if(extend) // allocate first block
+- *bkref = hole ? 0 : alloc_blk(fs);
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
++ }
++
++ /* 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);
++ b = (uint32*)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);
++ b = (uint32*)get_blk(fs, b[bw->bpind]);
++ if(extend) // allocate first indirect block
++ b[bw->bpdind] = alloc_blk(fs,nod);
++ b = (uint32*)get_blk(fs, b[bw->bpdind]);
++ bkref = &b[bw->bptind];
++ if(extend) // allocate first data block
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
++ }
++ /* 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 = (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]);
++ bkref = &b[bw->bptind];
++ if(extend) // allocate data block
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
++ }
++ /* 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 = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
++ b = (uint32*)get_blk(fs, b[bw->bpind]);
++ if (extend) // allocate single indirect block
++ b[bw->bpdind] = alloc_blk(fs,nod);
++ b = (uint32*)get_blk(fs, b[bw->bpdind]);
++ bkref = &b[bw->bptind];
++ if(extend) // allocate first data block
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
++ }
++ /* 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 = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
++ if(extend) // allocate double indirect block
++ b[bw->bpind] = alloc_blk(fs,nod);
++ b = (uint32*)get_blk(fs, b[bw->bpind]);
++ if(extend) // allocate single indirect block
++ b[bw->bpdind] = alloc_blk(fs,nod);
++ b = (uint32*)get_blk(fs, b[bw->bpdind]);
++ bkref = &b[bw->bptind];
++ if(extend) // allocate first block
++ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ }
+- // I don't do triple indirect - it's such a small filesystem ...
+ else
+- errexit("file too big ! blocks list for inode %d extends past double indirect blocks!", nod);
++ error_msg_and_die("file too big !");
++ /* End change for walking triple indirection */
++
+ if(*bkref)
+ {
+ bw->bnum++;
+- if(!allocated(fs->bbm, *bkref))
+- errexit("[block %d of inode %d is unallocated !]", *bkref, nod);
++ if(!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);
+ }
+ if(extend)
+ get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
+@@ -663,23 +938,40 @@
+ }
+
+ // link an entry (inode #) to a directory
+-void add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name)
++void add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name, uint32 mode, uid_t uid, gid_t gid, time_t ctime)
+ {
+ blockwalker bw;
+ uint32 bk;
+ uint8 *b;
+ directory *d;
+ int reclen, nlen;
+- if((get_nod(fs, dnod)->i_mode & FM_IFMT) != FM_IFDIR)
+- errexit("can't add '%s' to a non-directory", name);
++ inode *node;
++ inode *pnode;
++
++ /* Squash all permissions so files are owned by root
++ * and file permissions have group/other perms removed */
++ if (squash_uids) {
++ uid = gid = 0;
++ }
++ if (squash_perms) {
++ if (!S_ISLNK(mode)) {
++ mode &= ~(S_IWGRP | S_IWOTH);
++ mode &= ~(S_ISUID | S_ISGID);
++ }
++ }
++
++ pnode = get_nod(fs, dnod);
++
++ if(!S_ISDIR(pnode->i_mode))
++ error_msg_and_die("can't add '%s' to a non-directory", name);
+ if(!*name)
+- errexit("bad name '%s' (not meaningful)", name);
++ error_msg_and_die("bad name '%s' (not meaningful)", name);
+ if(strchr(name, '/'))
+- errexit("bad name '%s' (contains a slash)", name);
++ error_msg_and_die("bad name '%s' (contains a slash)", name);
+ nlen = strlen(name);
+ reclen = sizeof(directory) + rndup(nlen, 4);
+ if(reclen > BLOCKSIZE)
+- errexit("bad name '%s' (too long)", name);
++ error_msg_and_die("bad name '%s' (too long)", name);
+ init_bw(fs, dnod, &bw);
+ while((bk = walk_bw(fs, dnod, &bw, 0, 0)) != WALK_END) // for all blocks in dir
+ {
+@@ -691,9 +983,16 @@
+ if((!d->d_inode) && (d->d_rec_len >= reclen))
+ {
+ d->d_inode = nod;
+- get_nod(fs, nod)->i_links_count++;
++ node = get_nod(fs, nod);
++ node->i_links_count++;
+ d->d_name_len = nlen;
+- strncpy(d->d_name, name, nlen);
++ strncpy(d->d_name, name, rndup(nlen,4));
++ node->i_mode = mode;
++ node->i_uid = uid;
++ node->i_gid = gid;
++ node->i_atime = ctime;
++ node->i_ctime = ctime;
++ node->i_mtime = ctime;
+ return;
+ }
+ // if entry with enough room (last one?), shrink it & use it
+@@ -705,9 +1004,16 @@
+ d = (directory*) (((int8*)d) + d->d_rec_len);
+ d->d_rec_len = reclen;
+ d->d_inode = nod;
+- get_nod(fs, nod)->i_links_count++;
++ node = get_nod(fs, nod);
++ node->i_links_count++;
+ d->d_name_len = nlen;
+- strncpy(d->d_name, name, nlen);
++ strncpy(d->d_name, name, rndup(nlen,4));
++ node->i_mode = mode;
++ node->i_uid = uid;
++ node->i_gid = gid;
++ node->i_atime = ctime;
++ node->i_ctime = ctime;
++ node->i_mtime = ctime;
+ return;
+ }
+ }
+@@ -716,10 +1022,17 @@
+ b = get_workblk();
+ d = (directory*)b;
+ d->d_inode = nod;
+- get_nod(fs, nod)->i_links_count++;
++ node = get_nod(fs, nod);
++ node->i_links_count++;
+ d->d_rec_len = BLOCKSIZE;
+ d->d_name_len = nlen;
+- strncpy(d->d_name, name, nlen);
++ strncpy(d->d_name, name, rndup(nlen,4));
++ node->i_mode = mode;
++ node->i_uid = uid;
++ node->i_gid = gid;
++ node->i_atime = ctime;
++ node->i_ctime = ctime;
++ node->i_mtime = ctime;
+ extend_blk(fs, dnod, b, 1);
+ get_nod(fs, dnod)->i_size += BLOCKSIZE;
+ free_workblk(b);
+@@ -747,7 +1060,7 @@
+ // find the inode of a full path
+ uint32 find_path(filesystem *fs, uint32 nod, const char * name)
+ {
+- char *p, *n, *n2 = strdup(name);
++ char *p, *n, *n2 = xstrdup(name);
+ n = n2;
+ while(*n == '/')
+ {
+@@ -770,27 +1083,32 @@
+ }
+
+ // make a full-fledged directory (i.e. with "." & "..")
+-uint32 mkdir_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode)
++uint32 mkdir_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode,
++ uid_t uid, gid_t gid, time_t ctime)
+ {
+ uint32 nod;
+ if((nod = find_dir(fs, parent_nod, name)))
+ return nod;
+ nod = alloc_nod(fs);
+- get_nod(fs, nod)->i_mode = FM_IFDIR | mode;
+- add2dir(fs, parent_nod, nod, name);
+- add2dir(fs, nod, nod, ".");
+- add2dir(fs, nod, parent_nod, "..");
+- fs->gd.bg_used_dirs_count++;
++ if (!(mode & FM_IFDIR))
++ mode |= FM_IFDIR;
++ add2dir(fs, parent_nod, nod, name, mode, uid, gid, ctime);
++ add2dir(fs, nod, nod, ".", mode, uid, gid, ctime);
++ add2dir(fs, nod, parent_nod, "..", mode, uid, gid, ctime);
++ fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++;
+ return nod;
+ }
+
+ // make a symlink
+-uint32 mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 * b)
++uint32 mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size,
++ uint8 * b, uid_t uid, gid_t gid, time_t ctime)
+ {
++ uint32 mode;
+ uint32 nod = alloc_nod(fs);
++ mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
+ get_nod(fs, nod)->i_mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
+ get_nod(fs, nod)->i_size = size;
+- add2dir(fs, parent_nod, nod, name);
++ add2dir(fs, parent_nod, nod, name, mode, uid, gid, ctime);
+ if(size <= 4 * (EXT2_TIND_BLOCK+1))
+ {
+ strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size);
+@@ -801,15 +1119,15 @@
+ }
+
+ // make a file from a FILE*
+-uint32 mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f)
++uint32 mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, time_t ctime)
+ {
+ uint8 * b;
+ uint32 nod = alloc_nod(fs);
+- get_nod(fs, nod)->i_mode = FM_IFREG | mode;
++ mode |= FM_IFREG;
+ get_nod(fs, nod)->i_size = size;
+- add2dir(fs, parent_nod, nod, name);
++ add2dir(fs, parent_nod, nod, name, mode, uid, gid, ctime);
+ if(!(b = (uint8*)malloc(rndup(size, BLOCKSIZE))))
+- errexit("not enough mem to read file '%s'", name);
++ error_msg_and_die("not enough mem to read file '%s'", name);
+ memset(b, 0,rndup(size, BLOCKSIZE));
+ if(f)
+ fread(b, size, 1, f);
+@@ -824,6 +1142,15 @@
+ uint32 get_mode(struct stat *st)
+ {
+ uint32 mode = 0;
++
++ /* Squash file permissions as needed */
++ if (squash_perms) {
++ if (!S_ISLNK(mode)) {
++ st->st_mode &= ~(S_IWGRP | S_IWOTH);
++ st->st_mode &= ~(S_ISUID | S_ISGID);
++ }
++ }
++
+ if(st->st_mode & S_IRUSR)
+ mode |= FM_IRUSR | FM_IRGRP | FM_IROTH;
+ if(st->st_mode & S_IWUSR)
+@@ -833,30 +1160,17 @@
+ return mode;
+ }
+
+-// retrieves a mode info from a string
+-uint32 get_modestr(const char *p)
+-{
+- uint32 mode = 0;
+- if(p[0] == 'r')
+- mode |= FM_IRUSR | FM_IRGRP | FM_IROTH;
+- if(p[1] == 'w')
+- mode |= FM_IWUSR | FM_IWGRP | FM_IWOTH;
+- if(p[2] == 'x' || p[2] == 's')
+- mode |= FM_IXUSR | FM_IXGRP | FM_IXOTH;
+- return mode;
+-}
+-
+ // basename of a path - free me
+ char * basename(const char * fullpath)
+ {
+ char * p = strrchr(fullpath, '/');
+- return strdup(p ? p + 1 : fullpath);
++ return xstrdup(p ? p + 1 : fullpath);
+ }
+
+ // dirname of a path - free me
+ char * dirname(const char * fullpath)
+ {
+- char * p, * n = strdup(fullpath);
++ char * p, * n = xstrdup(fullpath);
+ if((p = strrchr(n, '/')))
+ *(p+1) = 0;
+ else
+@@ -864,66 +1178,6 @@
+ return n;
+ }
+
+-// adds entries to the filesystem from a text file
+-void add2fs_from_file(filesystem *fs, uint32 this_nod, FILE * fh)
+-{
+- uint32 mode;
+- uint32 nod, nod2;
+- char cmod[11], *path, *name, *dir;
+- int major, minor;
+- while(fscanf(fh, "%10s", cmod))
+- {
+- if(feof(fh))
+- break;
+- mode = get_modestr(cmod + 1);
+- switch(*cmod)
+- {
+- case 'd':
+- fscanf(fh, "%" SCANF_PREFIX "s\n", SCANF_STRING(path));
+- break;
+- case 'c':
+- mode |= FM_IFCHR;
+- fscanf(fh, "%i, %i %" SCANF_PREFIX "s\n", &major, &minor, SCANF_STRING(path));
+- break;
+- case 'b':
+- mode |= FM_IFBLK;
+- fscanf(fh, "%i, %i %" SCANF_PREFIX "s\n", &major, &minor, SCANF_STRING(path));
+- break;
+- case '#':
+- while(fgetc(fh) != '\n');
+- continue;
+- default:
+- errexit("malformed text input file");
+- }
+- name = basename(path);
+- dir = dirname(path);
+- free(path);
+- if(!(nod = find_path(fs, this_nod, dir)))
+- errexit("can't find directory '%s' to create '%s''", dir, name);
+- free(dir);
+- if((!strcmp(name, ".")) || (!strcmp(name, "..")))
+- {
+- free(name);
+- continue;
+- }
+- switch(*cmod)
+- {
+- case 'd':
+- mkdir_fs(fs, nod, name, mode);
+- break;
+- case 'c':
+- case 'b':
+- nod2 = alloc_nod(fs);
+- get_nod(fs, nod2)->i_mode = mode;
+- ((uint8*)get_nod(fs, nod2)->i_block)[0] = minor;
+- ((uint8*)get_nod(fs, nod2)->i_block)[1] = major;
+- add2dir(fs, nod, nod2, name);
+- break;
+- }
+- free(name);
+- }
+-}
+-
+ // adds a tree of entries to the filesystem from current dir
+ void add2fs_from_dir(filesystem *fs, uint32 this_nod)
+ {
+@@ -934,7 +1188,7 @@
+ struct stat st;
+ uint8 *b;
+ if(!(dh = opendir(".")))
+- pexit(".");
++ perror_msg_and_die(".");
+ while((dent = readdir(dh)))
+ {
+ if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
+@@ -948,31 +1202,27 @@
+ get_nod(fs, nod)->i_mode = (((st.st_mode & S_IFMT) == S_IFCHR) ? FM_IFCHR : FM_IFBLK) | get_mode(&st);
+ ((uint8*)get_nod(fs, nod)->i_block)[0] = (st.st_rdev & 0xff);
+ ((uint8*)get_nod(fs, nod)->i_block)[1] = (st.st_rdev >> 8);
+- add2dir(fs, this_nod, nod, dent->d_name);
++ add2dir(fs, this_nod, nod, dent->d_name, st.st_mode, st.st_uid, st.st_gid, st.st_ctime);
+ break;
+ case S_IFLNK:
+- if(!(b = (uint8*)malloc(rndup(st.st_size, BLOCKSIZE))))
+- errexit("out of memory");
+- if(readlink(dent->d_name, (char*)b, st.st_size) < 0)
+- pexit(dent->d_name);
+- mklink_fs(fs, this_nod, dent->d_name, st.st_size, b);
++ b = xreadlink(dent->d_name);
++ mklink_fs(fs, this_nod, dent->d_name, st.st_size, b, st.st_uid, st.st_gid, st.st_ctime);
+ free(b);
+ break;
+ case S_IFREG:
+- if(!(fh = fopen(dent->d_name, "r")))
+- pexit(dent->d_name);
+- mkfile_fs(fs, this_nod, dent->d_name, get_mode(&st), st.st_size, fh);
++ fh = xfopen(dent->d_name, "r");
++ mkfile_fs(fs, this_nod, dent->d_name, st.st_mode, st.st_size, fh, st.st_uid, st.st_gid, st.st_ctime);
+ fclose(fh);
+ break;
+ case S_IFDIR:
+- nod = mkdir_fs(fs, this_nod, dent->d_name, get_mode(&st));
++ nod = mkdir_fs(fs, this_nod, dent->d_name, st.st_mode, st.st_uid, st.st_gid, st.st_ctime);
+ if(chdir(dent->d_name) < 0)
+- pexit(dent->d_name);
++ perror_msg_and_die(dent->d_name);
+ add2fs_from_dir(fs, nod);
+ chdir("..");
+ break;
+ default:
+- fprintf(stderr, "ignoring entry %s", dent->d_name);
++ error_msg("ignoring entry %s", dent->d_name);
+ }
+ }
+ closedir(dh);
+@@ -981,9 +1231,11 @@
+ // endianness swap of x-indirect blocks
+ void swap_goodblocks(filesystem *fs, inode *nod)
+ {
+- int i;
++ int i,j,done=0;
++ uint32 *b,*b2;
++
+ int nblk = nod->i_blocks / INOBLK;
+- if((nod->i_size && !nblk) || (nod->i_mode & (FM_IFBLK | FM_IFCHR)))
++ 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)
+@@ -991,20 +1243,55 @@
+ swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
+ if(nblk <= EXT2_IND_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
++ */
++ assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
+ for(i = 0; i < BLOCKSIZE/4; i++)
++ /* Should this be...
++ if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
++ */
+ if(nblk > EXT2_IND_BLOCK + 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;
+- errexit("too big file on the filesystem");
++ /* 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;
++ }
++ }
++ swap_block((uint8 *)b2);
++ }
++ swap_block((uint8 *)b);
++ return;
+ }
+
+ void swap_badblocks(filesystem *fs, inode *nod)
+ {
+- int i;
++ int i,j,done=0;
++ uint32 *b,*b2;
++
+ int nblk = nod->i_blocks / INOBLK;
+- if((nod->i_size && !nblk) || (nod->i_mode & (FM_IFBLK | FM_IFCHR)))
++ 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)
+@@ -1012,13 +1299,34 @@
+ swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
+ if(nblk <= EXT2_IND_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++)
++ /* See comment in swap_goodblocks */
+ if(nblk > EXT2_IND_BLOCK + 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;
+- errexit("too big file on the filesystem");
++ /* 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;
+ }
+
+ // endianness swap of the whole filesystem
+@@ -1045,7 +1353,8 @@
+ swap_goodblocks(fs, nod);
+ swap_nod(nod);
+ }
+- swap_gd(&fs->gd);
++ for(i=0;i<GRP_NBGROUPS(fs);i++)
++ swap_gd(&(fs->gd[i]));
+ swap_sb(&fs->sb);
+ }
+
+@@ -1053,7 +1362,8 @@
+ {
+ int i;
+ swap_sb(&fs->sb);
+- swap_gd(&fs->gd);
++ 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);
+@@ -1084,53 +1394,118 @@
+ directory *d;
+ uint8 * b;
+ uint32 nod;
++ uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks,
++ free_blocks_per_group,nbblocks_per_group;
++ uint32 gd,itbl,ibmpos,bbmpos,itblpos;
++ int j;
++ uint8 *bbm,*ibm;
++ inode *itab0;
+
+ if(nbblocks < 16) // totally arbitrary
+- errexit("too small filesystem");
+- if(nbblocks >BLOCKS_PER_GROUP) // I build only one group
+- errexit("too big filesystem");
++ error_msg_and_die("too small filesystem");
++
++ /* nbblocks is the total number of blocks in the system. First
++ * calculate how much overhead blocks - inode table blocks,bitmap
++ * blocks,group descriptor blocks etc. - are needed assuming each
++ * group has BLOCKS_PER_GROUP blocks.Then recalculate nbblocks with
++ * this figure. Each group has the same number of blocks. So the fs
++ * has a size atleast the given value but usually rounded off to a i
++ * higher number.
++ */
++ nbgroups = rndup(nbblocks,BLOCKS_PER_GROUP)/ BLOCKS_PER_GROUP;
++ nbinodes_per_group = nbinodes/nbgroups +1;
++ nbinodes_per_group = rndup(nbinodes_per_group, BLOCKSIZE/sizeof(inode));
++ if (nbinodes_per_group < 16)
++ nbinodes_per_group = 16; //minimum number b'cos the first 10 are reserved
++ overhead_per_group = 3 /*super block,ibm,bbm*/
++ + /* No. of blocks that the inodes occupy */
++ nbinodes_per_group *sizeof(inode)/BLOCKSIZE
++ + /* No. of blocks that group descriptors occupy */
++ rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
++ free_blocks = nbblocks - overhead_per_group * nbgroups - 1 /*boot block */;
++ free_blocks_per_group = free_blocks/nbgroups;
++ if (free_blocks > free_blocks_per_group * nbgroups)
++ free_blocks_per_group++;
++ nbblocks_per_group = free_blocks_per_group + overhead_per_group;
++ /* e2fsck complains if nbblocks_per_group is not a multiple of 8 */
++ nbblocks_per_group = rndup(nbblocks_per_group,8);
++ free_blocks_per_group = nbblocks_per_group - overhead_per_group;
++ if (nbblocks_per_group > BLOCKS_PER_GROUP) {
++ /* Can this happen ? */
++ nbblocks_per_group = BLOCKS_PER_GROUP;
++ free_blocks_per_group = nbblocks_per_group - overhead_per_group;
++ }
++ nbblocks = nbblocks_per_group * nbgroups + 1;
++
++
+ if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
+- errexit("not enough memory for filesystem");
++ error_msg_and_die("not enough memory for filesystem");
+
+ // create the superblock for an empty filesystem
+- fs->sb.s_inodes_count = rndup(nbinodes, BLOCKSIZE/sizeof(inode));
++ 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 = nbblocks;
++ fs->sb.s_free_blocks_count = free_blocks_per_group*nbgroups;
+ fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
+ fs->sb.s_first_data_block = (BLOCKSIZE == 1024);
+ fs->sb.s_log_block_size = BLOCKSIZE >> 11;
+ fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
+- fs->sb.s_blocks_per_group = BLOCKS_PER_GROUP;
+- fs->sb.s_frags_per_group = BLOCKS_PER_GROUP;
+- fs->sb.s_inodes_per_group = fs->sb.s_inodes_count;
++ 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_magic = EXT2_MAGIC_NUMBER;
+
+ // set up groupdescriptors
+- fs->sb.s_free_blocks_count -= 5 + fs->sb.s_inodes_count * sizeof(inode) / BLOCKSIZE;
+- fs->gd.bg_free_blocks_count = fs->sb.s_free_blocks_count;
+- fs->gd.bg_free_inodes_count = fs->sb.s_free_inodes_count;
+- fs->gd.bg_used_dirs_count = 1;
+- fs->gd.bg_block_bitmap = 3;
+- fs->gd.bg_inode_bitmap = 4;
+- fs->gd.bg_inode_table = 5;
+-
+- // mark non-filesystem blocks and inodes as allocated
+- for(i = fs->sb.s_blocks_count; i <= BLOCKSIZE * 8; i++)
+- allocate(fs->bbm, i);
+- for(i = fs->sb.s_inodes_count + 1; i <= BLOCKSIZE * 8; i++)
+- allocate(fs->ibm, i);
+-
+- // mark system blocsk and inodes as allocated
+- for(i = 1; i <= 4 + fs->sb.s_inodes_count * sizeof(inode) / BLOCKSIZE; i++)
+- allocate(fs->bbm, i);
+- for(i = 1; i < EXT2_FIRST_INO; i++)
+- allocate(fs->ibm, i);
+-
+- // make root inode and directory
+- fs->itab[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRWXG | FM_IRWXO;
+- fs->itab[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
+- fs->itab[EXT2_ROOT_INO-1].i_links_count = 2;
++ gd = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
++ itbl = nbinodes_per_group*sizeof(inode)/BLOCKSIZE;
++ for(i = 0,bbmpos=2+gd,ibmpos=3+gd,itblpos =4+gd;
++ i<nbgroups;
++ i++, bbmpos += nbblocks_per_group,ibmpos += nbblocks_per_group,
++ itblpos += nbblocks_per_group) {
++
++ fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
++ fs->gd[i].bg_free_inodes_count = nbinodes_per_group;
++ 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<nbgroups;i++) {
++
++ /* Block bitmap */
++ bbm = get_blk(fs,fs->gd[i].bg_block_bitmap);
++ //non-filesystem blocks.
++ for(j=fs->sb.s_blocks_per_group + 1; j <= BLOCKSIZE * 8; j++)
++ allocate(bbm, j);
++ //system blocks
++ for(j = 1; j <= 3+gd+itbl; 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);
++ }
++
++ /* We have groups now. Add the root filesystem in group 0 */
++ /* Also allocate the system inodes in group 0 and update */
++ /* directory count and inode count for group 0 */
++
++ ibm = get_blk(fs,fs->gd[0].bg_inode_bitmap);
++ for(j = 1; j < EXT2_FIRST_INO; j++) {
++ allocate(ibm, j);
++ 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_IRWXG | FM_IRWXO;
++ itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
++ itab0[EXT2_ROOT_INO-1].i_links_count = 2;
++
+ b = get_workblk();
+ d = (directory*)b;
+ d->d_inode = EXT2_ROOT_INO;
+@@ -1147,9 +1522,14 @@
+ // make lost+found directory and reserve blocks
+ if(fs->sb.s_r_blocks_count)
+ {
+- nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU | FM_IRWXG | FM_IRWXO);
++ nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, 0, 0, time(NULL));
+ 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 > 2049 )
++ fs->sb.s_r_blocks_count=2049;
+ 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;
+@@ -1170,24 +1550,24 @@
+ // loads a filesystem from disk
+ filesystem * load_fs(FILE * fh, int swapit)
+ {
+- size_t fssize;
++ size_t fssize = 0;
+ filesystem *fs;
+ if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftell(fh)) < 0))
+- pexit("input filesystem image");
++ perror_msg_and_die("input filesystem image");
+ rewind(fh);
+ fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
+ if(fssize < 16) // totally arbitrary
+- errexit("too small filesystem");
+- if(fssize > BLOCKS_PER_GROUP) // I build only one group
+- errexit("too big filesystem");
++ error_msg_and_die("too small filesystem");
++/* if(fssize > BLOCKS_PER_GROUP) // I build only one group
++ error_msg_and_die("too big filesystem"); */
+ if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
+- errexit("not enough memory for filesystem");
++ error_msg_and_die("not enough memory for filesystem");
+ if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
+- pexit("input filesystem image");
++ perror_msg_and_die("input filesystem image");
+ if(swapit)
+ swap_badfs(fs);
+ if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
+- errexit("not a suitable ext2 filesystem");
++ error_msg_and_die("not a suitable ext2 filesystem");
+ return fs;
+ }
+
+@@ -1230,9 +1610,9 @@
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ {
+ if(fsize <= 0)
+- errexit("wrong size while saving inode %d", nod);
++ error_msg_and_die("wrong size while saving inode %d", nod);
+ if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
+- errexit("error while saving inode %d", nod);
++ error_msg_and_die("error while saving inode %d", nod);
+ fsize -= BLOCKSIZE;
+ }
+ }
+@@ -1250,7 +1630,7 @@
+ {
+ int i, j;
+ if(fsize <= 0)
+- errexit("wrong size while saving inode %d", nod);
++ error_msg_and_die("wrong size while saving inode %d", nod);
+ b = get_blk(fs, bk);
+ for(i = 0; i < 64; i++)
+ {
+@@ -1406,7 +1786,7 @@
+ 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(fs->ibm, nod))
++ if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod)))
+ {
+ printf("unallocated\n");
+ return;
+@@ -1440,24 +1820,46 @@
+ default:
+ list_blocks(fs, nod);
+ }
++ printf("Done with inode %d\n",nod);
+ }
+
+ // describes various fields in a filesystem
+ void print_fs(filesystem *fs)
+ {
+- int i;
+- 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);
+- 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);
+- 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);
+- printf("block bitmap: block %d, inode bitmap: block %d, inode table: block %d\n", fs->gd.bg_block_bitmap, fs->gd.bg_inode_bitmap, fs->gd.bg_inode_table);
+- printf("block bitmap allocation:\n");
+- print_bm(fs->bbm, fs->sb.s_blocks_count);
+- printf("inode bitmap allocation:\n");
+- print_bm(fs->ibm, fs->sb.s_inodes_count);
+- for(i=1; i<=fs->sb.s_inodes_count; i++)
+- if(allocated(fs->ibm, i))
+- print_inode(fs, i);
++ int i,j;
++ 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);
++ 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);
++ 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);
++ printf("Size of inode table: %d blocks\n",
++ fs->sb.s_inodes_per_group * sizeof(inode)/BLOCKSIZE);
++ for (i = 0; i < GRP_NBGROUPS(fs); i++) {
++ printf("Group No: %d\n", i);
++ 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);
++ printf("Free blocks count: %d\n",fs->gd[i].bg_free_blocks_count);
++ printf("Free inodes count: %d\n",fs->gd[i].bg_free_inodes_count);
++ printf("Used dir count: %d\n",fs->gd[i].bg_used_dirs_count);
++ printf("block bitmap allocation:\n");
++ print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group);
++ printf("inode bitmap allocation:\n");
++ ibm = GRP_GET_GROUP_IBM(fs, i);
++ print_bm(ibm, fs->sb.s_inodes_per_group);
++ for (j = 1; j <= fs->sb.s_inodes_per_group; j++)
++ if (allocated(ibm, j))
++ print_inode(fs, i*fs->sb.s_inodes_per_group + j);
++ }
+ }
+
+ void dump_fs(filesystem *fs, FILE * fh, int swapit)
+@@ -1467,31 +1869,234 @@
+ if(swapit)
+ swap_goodfs(fs);
+ if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
+- pexit("output filesystem image");
++ perror_msg_and_die("output filesystem image");
+ if(swapit)
+ swap_badfs(fs);
+ }
+
++/* device table entries take the form of:
++ <path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
++ /dev/mem c 640 0 0 1 1 0 0 -
++
++ type can be one of:
++ f A regular file
++ d Directory
++ c Character special device file
++ b Block special device file
++ p Fifo (named pipe)
++
++ I don't bother with symlinks (permissions are irrelevant), hard
++ links (special cases of regular files), or sockets (why bother).
++
++ Regular files must exist in the target root directory. If a char,
++ block, fifo, or directory does not exist, it will be created.
++*/
++static int interpret_table_entry(filesystem *fs, char *line)
++{
++ char type, *name = NULL, *tmp, *dir, *bname;
++ unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
++ unsigned long start = 0, increment = 1, count = 0;
++ inode *entry;
++ uint32 nod, parent;
++
++ if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
++ SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor,
++ &start, &increment, &count) < 0)
++ {
++ return 1;
++ }
++
++ if (!strcmp(name, "/")) {
++ error_msg_and_die("Device table entries require absolute paths");
++ }
++
++ /* Check if this file already exists... */
++ switch (type) {
++ case 'd':
++ mode |= S_IFDIR;
++ break;
++ case 'f':
++ mode |= S_IFREG;
++ break;
++ case 'p':
++ mode |= S_IFIFO;
++ break;
++ case 'c':
++ mode |= S_IFCHR;
++ break;
++ case 'b':
++ mode |= S_IFBLK;
++ break;
++ default:
++ error_msg_and_die("Unsupported file type");
++ }
++ nod = 0;
++ if (count==0)
++ nod = find_path(fs, EXT2_ROOT_INO, name);
++ if (nod) {
++ /* Ok, we just need to fixup an existing entry
++ * and we will be all done... */
++ entry = get_nod(fs, nod);
++ entry->i_uid = uid;
++ entry->i_gid = gid;
++ entry->i_mode = mode;
++ if (major) {
++ dev_t rdev = makedev(major, minor);
++ ((uint8*)entry->i_block)[0] = (rdev & 0xff);
++ ((uint8*)entry->i_block)[1] = (rdev >> 8);
++ }
++ } else {
++ /* Try and find our parent now */
++ tmp = xstrdup(name);
++ dir = dirname(tmp);
++ parent = find_path(fs, EXT2_ROOT_INO, dir);
++ free(tmp);
++ if (!parent) {
++ error_msg ("skipping device_table entry '%s': no parent directory!", name);
++ free(name);
++ return 1;
++ }
++
++ tmp = xstrdup(name);
++ bname = xstrdup(basename(tmp));
++ free(tmp);
++ switch (type) {
++ case 'd':
++ mkdir_fs(fs, parent, bname, mode|FM_IFDIR, uid, gid, time(NULL));
++ break;
++ case 'f':
++#if 0
++ {
++ // This is a bit odd.. This will try to include
++ // the file of the same name from your _build_
++ // system... Probably a very bad idea....
++ struct stat st;
++ FILE *fh = xfopen(name, "r");
++ lstat(name, &st);
++ mkfile_fs(fs, parent, bname, mode|FM_IFREG, st.st_size, fh, uid, gid, st.st_ctime);
++ fclose(fh);
++ }
++#else
++ error_msg("ignoring entry %s", name);
++#endif
++ break;
++ case 'p':
++ error_msg("ignoring entry %s", name);
++ break;
++ case 'c':
++ case 'b':
++ if (count > 0) {
++ dev_t rdev;
++ char *dname;
++ unsigned long i;
++ for (i = start; i < count; i++) {
++ asprintf(&dname, "%s%lu", bname, i);
++ nod = find_path(fs, EXT2_ROOT_INO, dname);
++ if (nod) {
++ /* We just need to fixup an existing entry */
++ entry = get_nod(fs, nod);
++ } else {
++ nod = alloc_nod(fs);
++ add2dir(fs, parent, nod, dname, mode, uid, gid, time(NULL));
++ entry = get_nod(fs, nod);
++ }
++ entry->i_uid = uid;
++ entry->i_gid = gid;
++ entry->i_mode = mode;
++ rdev = makedev(major, minor + (i * increment - start));
++ ((uint8*)entry->i_block)[0] = (rdev & 0xff);
++ ((uint8*)entry->i_block)[1] = (rdev >> 8);
++ free(dname);
++ }
++ } else {
++ dev_t rdev = makedev(major, minor);
++ nod = alloc_nod(fs);
++ add2dir(fs, parent, nod, bname, mode, uid, gid, time(NULL));
++ entry = get_nod(fs, nod);
++ ((uint8*)entry->i_block)[0] = (rdev & 0xff);
++ ((uint8*)entry->i_block)[1] = (rdev >> 8);
++ }
++ break;
++ default:
++ error_msg_and_die("Unsupported file type");
++ }
++ free(bname);
++ }
++ free(name);
++ return 0;
++}
++
++static int parse_device_table(filesystem *root, FILE * file)
++{
++ char *line;
++ int status = 0;
++ size_t length = 0;
++
++ /* Turn off squash, since we must ensure that values
++ * entered via the device table are not squashed */
++ squash_uids = 0;
++ squash_perms = 0;
++
++ /* Looks ok so far. The general plan now is to read in one
++ * line at a time, check for leading comment delimiters ('#'),
++ * then try and parse the line as a device table. If we fail
++ * to parse things, try and help the poor fool to fix their
++ * device table with a useful error msg... */
++ line = NULL;
++ while (getline(&line, &length, file) != -1) {
++ /* First trim off any whitespace */
++ int len = strlen(line);
++
++ /* trim trailing whitespace */
++ while (len > 0 && isspace(line[len - 1]))
++ line[--len] = '\0';
++ /* trim leading whitespace */
++ memmove(line, &line[strspn(line, " \n\r\t\v")], len);
++
++ /* How long are we after trimming? */
++ len = strlen(line);
++
++ /* If this is NOT a comment line, try to interpret it */
++ if (len && *line != '#') {
++ if (interpret_table_entry(root, line))
++ status = 1;
++ }
++
++ free(line);
++ line = NULL;
++ }
++ fclose(file);
++
++ return status;
++}
++
++/*
++Local Variables:
++c-file-style: "linux"
++c-basic-offset: 4
++tab-width: 4
++End:
++*/
++
+ void showhelp(void)
+ {
+ fprintf(stderr, "Usage: %s [options] image\n"
+ "Create an ext2 filesystem image from directories/files\n\n"
+- " -x image Use this image as a starting point\n"
+- " -d directory Add this directory as source\n"
+- " -f file Add nodes (e.g. devices) from this spec file\n"
+- " -b blocks Size in blocks\n"
+- " -i inodes Number of inodes\n"
+- " -r reserved Number of reserved blocks\n"
+- " -g path Generate a block map file for this path\n"
+- " -e value Fill unallocated blocks with value\n"
+- " -z Make files with holes\n"
+- " -v Print resulting filesystem structure\n"
+- " -h Show this help\n\n"
+- "Example of spec file:\n"
+- "drwx /dev\n"
+- "crw- 10,190 /dev/lcd\n"
+- "brw- 1,0 /dev/ram0\n\n"
+- "Report bugs to xavier.bestel@free.fr\n", argv0);
++ " -x image Use this image as a starting point\n"
++ " -d directory Add this directory as source\n"
++ " -b blocks Size in blocks\n"
++ " -i inodes Number of inodes\n"
++ " -r reserved Number of reserved blocks\n"
++ " -g path Generate a block map file for this path\n"
++ " -e value Fill unallocated blocks with value\n"
++ " -z Make files with holes\n"
++ " -D,-f Use the named FILE as a device table file\n"
++ " -q Squash permissions and owners making all files be owned by root\n"
++ " -U Squash owners making all files be owned by root\n"
++ " -P Squash permissions on all files\n"
++ " -v Print resulting filesystem structure\n"
++ " -h Show this help\n\n"
++ "Report bugs to xavier.bestel@free.fr\n", app_name);
+ }
+
+ #define MAX_DOPT 128
+@@ -1521,21 +2126,17 @@
+ filesystem *fs;
+ int i;
+ int c;
++ struct stat sb;
++ FILE *devtable = NULL;
+
+- argv0 = argv[0];
+- if(argc <= 1)
+- {
+- showhelp();
+- exit(1);
+- }
+- while((c = getopt(argc, argv, "x:f:d:b:i:r:g:e:zvh")) != EOF)
++ app_name = argv[0];
++ while((c = getopt(argc, argv, "x:d:b:i:r:g:e:zvhD:f:qUP")) != EOF)
+ switch(c)
+ {
+ case 'x':
+ fsin = optarg;
+ break;
+ case 'd':
+- case 'f':
+ dopt[didx++] = optarg;
+ break;
+ case 'b':
+@@ -1556,6 +2157,24 @@
+ case 'z':
+ holes = 1;
+ break;
++ case 'f':
++ case 'D':
++ devtable = xfopen(optarg, "r");
++ if (fstat(fileno(devtable), &sb) < 0)
++ perror_msg_and_die(optarg);
++ if (sb.st_size < 10)
++ error_msg_and_die("%s: not a proper device table file", optarg);
++ break;
++ case 'q':
++ squash_uids = 1;
++ squash_perms = 1;
++ break;
++ case 'U':
++ squash_uids = 1;
++ break;
++ case 'P':
++ squash_perms = 1;
++ break;
+ case 'v':
+ verbose = 1;
+ break;
+@@ -1566,16 +2185,14 @@
+ exit(1);
+ }
+ if(optind < (argc - 1))
+- errexit("too many arguments");
++ error_msg_and_die("too many arguments");
+ if(optind == (argc - 1))
+ fsout = argv[optind];
+ if(fsin)
+ {
+ if(strcmp(fsin, "-"))
+ {
+- FILE * fh = fopen(fsin, "r");
+- if(!fh)
+- pexit(fsin);
++ FILE * fh = xfopen(fsin, "r");
+ fs = load_fs(fh, bigendian);
+ fclose(fh);
+ }
+@@ -1585,7 +2202,7 @@
+ else
+ {
+ if(nbblocks == -1)
+- errexit("filesystem size unspecified");
++ error_msg_and_die("filesystem size unspecified");
+ if(nbinodes == -1)
+ nbinodes = nbblocks * BLOCKSIZE / rndup(BYTES_PER_INODE, BLOCKSIZE);
+ if(nbresrvd == -1)
+@@ -1595,35 +2212,30 @@
+ for(i = 0; i < didx; i++)
+ {
+ struct stat st;
+- FILE *fh;
+ char *pdir;
+ stat(dopt[i], &st);
+ switch(st.st_mode & S_IFMT)
+ {
+- case S_IFREG:
+- if(!(fh = fopen(dopt[i], "r")))
+- pexit(dopt[i]);
+- add2fs_from_file(fs, EXT2_ROOT_INO, fh);
+- fclose(fh);
+- break;
+ case S_IFDIR:
+ if(!(pdir = getcwd(0, GETCWD_SIZE)))
+- pexit(dopt[i]);
++ perror_msg_and_die(dopt[i]);
+ if(chdir(dopt[i]) < 0)
+- pexit(dopt[i]);
++ perror_msg_and_die(dopt[i]);
+ add2fs_from_dir(fs, EXT2_ROOT_INO);
+ if(chdir(pdir) < 0)
+- pexit(pdir);
++ perror_msg_and_die(pdir);
+ free(pdir);
+ break;
+ default:
+- errexit("%s in neither a file nor a directory", dopt[i]);
++ error_msg_and_die("%s is neither a file nor a directory", dopt[i]);
+ }
+ }
+ if(emptyval)
+ for(i = 1; i < fs->sb.s_blocks_count; i++)
+- if(!allocated(fs->bbm, i))
++ if(!allocated(GRP_GET_BLOCK_BITMAP(fs,i),GRP_BBM_OFFSET(fs,i)))
+ memset(get_blk(fs, i), emptyval, BLOCKSIZE);
++ if(devtable)
++ parse_device_table(fs, devtable);
+ if(verbose)
+ print_fs(fs);
+ for(i = 0; i < gidx; i++)
+@@ -1633,21 +2245,18 @@
+ char *p;
+ FILE *fh;
+ if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i])))
+- errexit("path %s not found in filesystem", 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]);
+- if(!(fh = fopen(fname, "w")))
+- pexit(fname);
++ fh = xfopen(fname, "w");
+ fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
+ flist_blocks(fs, nod, fh);
+ fclose(fh);
+ }
+ if(strcmp(fsout, "-"))
+ {
+- FILE * fh = fopen(fsout, "w");
+- if(!fh)
+- pexit(fsout);
++ FILE * fh = xfopen(fsout, "w");
+ dump_fs(fs, fh, bigendian);
+ fclose(fh);
+ }
+diff -urN genext2fs-1.3.orig/test-mount.sh genext2fs-1.3/test-mount.sh
+diff -urN genext2fs-1.3.orig/test.sh genext2fs-1.3/test.sh
diff --git a/recipes/genext2fs/genext2fs-native_1.3+1.4rc1.bb b/recipes/genext2fs/genext2fs-native_1.3+1.4rc1.bb
new file mode 100644
index 0000000000..50b371b97e
--- /dev/null
+++ b/recipes/genext2fs/genext2fs-native_1.3+1.4rc1.bb
@@ -0,0 +1,10 @@
+require genext2fs_${PV}.bb
+inherit native
+
+do_stage () {
+ install -m 0755 genext2fs ${STAGING_BINDIR}/
+}
+
+do_install () {
+ :
+}
diff --git a/recipes/genext2fs/genext2fs-native_1.3+cvs.bb b/recipes/genext2fs/genext2fs-native_1.3+cvs.bb
new file mode 100644
index 0000000000..50b371b97e
--- /dev/null
+++ b/recipes/genext2fs/genext2fs-native_1.3+cvs.bb
@@ -0,0 +1,10 @@
+require genext2fs_${PV}.bb
+inherit native
+
+do_stage () {
+ install -m 0755 genext2fs ${STAGING_BINDIR}/
+}
+
+do_install () {
+ :
+}
diff --git a/recipes/genext2fs/genext2fs-native_1.3.bb b/recipes/genext2fs/genext2fs-native_1.3.bb
new file mode 100644
index 0000000000..2e26fa0110
--- /dev/null
+++ b/recipes/genext2fs/genext2fs-native_1.3.bb
@@ -0,0 +1,11 @@
+require genext2fs_${PV}.bb
+inherit native
+FILESPATH = "${@base_set_filespath([ '${FILE_DIRNAME}/genext2fs-${PV}', '${FILE_DIRNAME}/genext2fs', '${FILE_DIRNAME}/files', '${FILE_DIRNAME}' ], d)}"
+
+do_stage () {
+ install -m 0755 genext2fs ${STAGING_BINDIR}/
+}
+
+do_install () {
+ :
+}
diff --git a/recipes/genext2fs/genext2fs.inc b/recipes/genext2fs/genext2fs.inc
new file mode 100644
index 0000000000..c2984bccdd
--- /dev/null
+++ b/recipes/genext2fs/genext2fs.inc
@@ -0,0 +1,4 @@
+LICENSE = "GPL"
+DESCRIPTION = "A tool to generate an ext2 filesystem \
+as a normal (non-root) user."
+SECTION = "console/utils"
diff --git a/recipes/genext2fs/genext2fs_1.3+1.4rc1.bb b/recipes/genext2fs/genext2fs_1.3+1.4rc1.bb
new file mode 100644
index 0000000000..9723036871
--- /dev/null
+++ b/recipes/genext2fs/genext2fs_1.3+1.4rc1.bb
@@ -0,0 +1,10 @@
+require genext2fs.inc
+
+TRIMMEDV = "${@bb.data.getVar('PV', d, 1).split('+')[1]}"
+FILESPATH = "${FILE_DIRNAME}/genext2fs-${PV}:${FILE_DIRNAME}/genext2fs:${FILE_DIRNAME}/files"
+SRC_URI = "${SOURCEFORGE_MIRROR}/genext2fs/genext2fs-${TRIMMEDV}.tar.gz \
+ file://volume.patch;patch=1"
+S = "${WORKDIR}/genext2fs-${TRIMMEDV}"
+DEFAULT_PREFERENCE = "1"
+
+inherit autotools
diff --git a/recipes/genext2fs/genext2fs_1.3+cvs.bb b/recipes/genext2fs/genext2fs_1.3+cvs.bb
new file mode 100644
index 0000000000..82af2041ab
--- /dev/null
+++ b/recipes/genext2fs/genext2fs_1.3+cvs.bb
@@ -0,0 +1,9 @@
+require genext2fs.inc
+
+PV_append = "${SRCDATE}"
+PR = "r3"
+FILESPATH = "${FILE_DIRNAME}/genext2fs-1.3+cvs:${FILE_DIRNAME}/genext2fs:${FILE_DIRNAME}/files"
+SRC_URI = "cvs://anonymous:@genext2fs.cvs.sourceforge.net/cvsroot/genext2fs;module=genext2fs"
+S = "${WORKDIR}/genext2fs"
+
+inherit autotools
diff --git a/recipes/genext2fs/genext2fs_1.3.bb b/recipes/genext2fs/genext2fs_1.3.bb
new file mode 100644
index 0000000000..e57f78ae50
--- /dev/null
+++ b/recipes/genext2fs/genext2fs_1.3.bb
@@ -0,0 +1,15 @@
+require genext2fs.inc
+
+PR = "r3"
+
+SRC_URI = "${DEBIAN_MIRROR}/main/g/genext2fs/genext2fs_${PV}.orig.tar.gz \
+ file://misc.patch;patch=1"
+S = "${WORKDIR}/genext2fs-${PV}.orig"
+
+do_compile () {
+ oe_runmake
+}
+
+do_install () {
+ oe_runmake 'DESTDIR=${D}' install
+}