--- cramfs-1.1.orig/cramfsck.c	2002-02-22 17:00:42.000000000 -0700
+++ cramfs-1.1/cramfsck.c	2002-12-21 01:25:17.000000000 -0700
@@ -51,10 +51,11 @@
 #include <utime.h>
 #include <sys/ioctl.h>
 #define _LINUX_STRING_H_
-#include <linux/fs.h>
-#include <linux/cramfs_fs.h>
+#include "linux/cramfs_fs.h"
 #include <zlib.h>
 
+#define BLKGETSIZE	_IO(0x12,96) /* return device size /512 (long *arg) */
+
 /* Exit codes used by fsck-type programs */
 #define FSCK_OK          0	/* No errors */
 #define FSCK_NONDESTRUCT 1	/* File system errors corrected */
@@ -75,7 +76,7 @@
 static int opt_verbose = 0;	/* 1 = verbose (-v), 2+ = very verbose (-vv) */
 #ifdef INCLUDE_FS_TESTS
 static int opt_extract = 0;		/* extract cramfs (-x) */
-static char *extract_dir = "root";	/* extraction directory (-x) */
+static char *extract_dir = "/";	/* extraction directory (-x) */
 static uid_t euid;			/* effective UID */
 
 /* (cramfs_super + start) <= start_dir < end_dir <= start_data <= end_data */
@@ -155,7 +156,7 @@
 	}
 
 	if (*length < sizeof(struct cramfs_super)) {
-		die(FSCK_UNCORRECTED, 0, "file length too short");
+		die(FSCK_UNCORRECTED, 0, "filesystem smaller than a cramfs superblock!");
 	}
 
 	/* find superblock */
@@ -190,7 +191,8 @@
 			die(FSCK_UNCORRECTED, 0, "zero file count");
 		}
 		if (*length < super.size) {
-			die(FSCK_UNCORRECTED, 0, "file length too short");
+			die(FSCK_UNCORRECTED, 0, "file length too short, %lu is smaller than %lu",
+				*length, super.size);
 		}
 		else if (*length > super.size) {
 			fprintf(stderr, "warning: file extends past end of filesystem\n");
@@ -267,11 +269,11 @@
 #ifdef INCLUDE_FS_TESTS
 static void print_node(char type, struct cramfs_inode *i, char *name)
 {
-	char info[10];
+	char info[11];
 
 	if (S_ISCHR(i->mode) || (S_ISBLK(i->mode))) {
 		/* major/minor numbers can be as high as 2^12 or 4096 */
-		snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size));
+		snprintf(info, 11, "%4d,%4d", major(i->size), minor(i->size));
 	}
 	else {
 		/* size be as high as 2^24 or 16777216 */
@@ -445,8 +447,10 @@
 	}
 	/* TODO: Do we need to check end_dir for empty case? */
 	memcpy(newpath, path, pathlen);
-	newpath[pathlen] = '/';
-	pathlen++;
+	if (pathlen > 1) {
+	    newpath[pathlen] = '/';
+	    pathlen++;
+	}
 	if (opt_verbose) {
 		print_node('d', i, path);
 	}
--- cramfs-1.1.orig/device_table.txt	1969-12-31 17:00:00.000000000 -0700
+++ cramfs-1.1/device_table.txt	2003-01-01 05:13:44.000000000 -0700
@@ -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 mkcramfs.  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	-
+
--- cramfs-1.1.orig/mkcramfs.c	2002-02-20 01:03:32.000000000 -0700
+++ cramfs-1.1/mkcramfs.c	2002-12-21 01:25:17.000000000 -0700
@@ -1,3 +1,4 @@
+/* vi: set sw=8 ts=8: */
 /*
  * mkcramfs - make a cramfs file system
  *
@@ -16,12 +17,21 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Added device table support (code taken from mkfs.jffs2.c, credit to
+ * Erik Andersen <andersen@codepoet.org>) as well as an option to squash
+ * permissions. - Russ Dill <Russ.Dill@asu.edu> September 2002
+ *
+ * Reworked, cleaned up, and updated for cramfs-1.1, December 2002
+ *  - Erik Andersen <andersen@codepoet.org>
+ *
  */
 
 /*
  * If you change the disk format of cramfs, please update fs/cramfs/README.
  */
 
+#define _GNU_SOURCE
 #include <sys/types.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -33,8 +43,15 @@
 #include <errno.h>
 #include <string.h>
 #include <stdarg.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <assert.h>
+#include <getopt.h>
 #include <linux/cramfs_fs.h>
 #include <zlib.h>
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
 
 /* Exit codes used by mkfs-type programs */
 #define MKFS_OK          0	/* No errors */
@@ -71,11 +88,17 @@
 		  + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \
 		  + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ )
 
+
+/* The kernel assumes PAGE_CACHE_SIZE as block size. */
+#define PAGE_CACHE_SIZE (4096)
+
+
 static const char *progname = "mkcramfs";
 static unsigned int blksize = PAGE_CACHE_SIZE;
 static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
 static int image_length = 0;
 
+
 /*
  * If opt_holes is set, then mkcramfs can create explicit holes in the
  * data, which saves 26 bytes per hole (which is a lot smaller a
@@ -91,10 +114,12 @@
 static int opt_holes = 0;
 static int opt_pad = 0;
 static int opt_verbose = 0;
+static int opt_squash = 0;
 static char *opt_image = NULL;
 static char *opt_name = NULL;
 
 static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid;
+static const char *const memory_exhausted = "memory exhausted";
 
 /* In-core version of inode / directory entry. */
 struct entry {
@@ -123,7 +148,7 @@
 {
 	FILE *stream = status ? stderr : stdout;
 
-	fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n"
+	fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] [-D file] dirname outfile\n"
 		" -h         print this help\n"
 		" -E         make all warnings errors (non-zero exit status)\n"
 		" -e edition set edition number (part of fsid)\n"
@@ -133,39 +158,157 @@
 		" -s         sort directory entries (old option, ignored)\n"
 		" -v         be more verbose\n"
 		" -z         make explicit holes (requires >= 2.3.39)\n"
-		" dirname    root of the directory tree to be compressed\n"
+		" -D         Use the named FILE as a device table file\n"
+		" -q         squash permissions (make everything owned by root)\n"
+		" dirname    root of the filesystem to be compressed\n"
 		" outfile    output file\n", progname, PAD_SIZE);
 
 	exit(status);
 }
 
-static void die(int status, int syserr, const char *fmt, ...)
+static void verror_msg(const char *s, va_list p)
+{
+	fflush(stdout);
+	fprintf(stderr, "mkcramfs: ");
+	vfprintf(stderr, s, p);
+}
+
+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));
+}
+
+static void perror_msg(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	vperror_msg(s, p);
+	va_end(p);
+}
+
+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(MKFS_ERROR);
+}
+
+static void perror_msg_and_die(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	vperror_msg(s, p);
+	va_end(p);
+	exit(MKFS_ERROR);
+}
+#ifndef DMALLOC
+extern 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 *xmalloc(size_t size)
+{
+	void *ptr = malloc(size);
+
+	if (ptr == NULL && size != 0)
+		error_msg_and_die(memory_exhausted);
+	return ptr;
+}
+
+extern void *xcalloc(size_t nmemb, size_t size)
+{
+	void *ptr = calloc(nmemb, size);
+
+	if (ptr == NULL && nmemb != 0 && size != 0)
+		error_msg_and_die(memory_exhausted);
+	return ptr;
+}
+
+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;
+}
+#endif
+
+static FILE *xfopen(const char *path, const char *mode)
 {
-	va_list arg_ptr;
-	int save = errno;
+	FILE *fp;
+
+	if ((fp = fopen(path, mode)) == NULL)
+		perror_msg_and_die("%s", path);
+	return fp;
+}
 
-	fflush(0);
-	va_start(arg_ptr, fmt);
-	fprintf(stderr, "%s: ", progname);
-	vfprintf(stderr, fmt, arg_ptr);
-	if (syserr) {
-		fprintf(stderr, ": %s", strerror(save));
+extern int xopen(const char *pathname, int flags, mode_t mode)
+{
+	int ret;
+	
+	if (flags & O_CREAT)
+		ret = open(pathname, flags, mode);
+	else
+		ret = open(pathname, flags);
+	if (ret == -1) {
+		perror_msg_and_die("%s", pathname);
 	}
-	fprintf(stderr, "\n");
-	va_end(arg_ptr);
-	exit(status);
+	return ret;
 }
 
+extern 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("%s:%s", progname, path);
+		    return NULL;
+		}
+	}           
+	while (bufsize < readsize + 1);
+
+	buf[readsize] = '\0';
+
+	return buf;
+}       
+
 static void map_entry(struct entry *entry)
 {
 	if (entry->path) {
 		entry->fd = open(entry->path, O_RDONLY);
 		if (entry->fd < 0) {
-			die(MKFS_ERROR, 1, "open failed: %s", entry->path);
+			error_msg_and_die("open failed: %s", entry->path);
 		}
 		entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, entry->fd, 0);
 		if (entry->uncompressed == MAP_FAILED) {
-			die(MKFS_ERROR, 1, "mmap failed: %s", entry->path);
+			error_msg_and_die("mmap failed: %s", entry->path);
 		}
 	}
 }
@@ -174,8 +317,9 @@
 {
 	if (entry->path) {
 		if (munmap(entry->uncompressed, entry->size) < 0) {
-			die(MKFS_ERROR, 1, "munmap failed: %s", entry->path);
+			error_msg_and_die("munmap failed: %s", entry->path);
 		}
+		entry->uncompressed=NULL;
 		close(entry->fd);
 	}
 }
@@ -204,7 +348,8 @@
 		find_identical_file(orig->next, newfile));
 }
 
-static void eliminate_doubles(struct entry *root, struct entry *orig) {
+static void eliminate_doubles(struct entry *root, struct entry *orig) 
+{
 	if (orig) {
 		if (orig->size && (orig->path || orig->uncompressed))
 			find_identical_file(root, orig);
@@ -232,10 +377,7 @@
 
 	/* Set up the path. */
 	/* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
-	path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1);
-	if (!path) {
-		die(MKFS_ERROR, 1, "malloc failed");
-	}
+	path = xmalloc(len + 1 + MAX_INPUT_NAMELEN + 1);
 	memcpy(path, name, len);
 	endpath = path + len;
 	*endpath = '/';
@@ -245,7 +387,7 @@
 	dircount = scandir(name, &dirlist, 0, cramsort);
 
 	if (dircount < 0) {
-		die(MKFS_ERROR, 1, "scandir failed: %s", name);
+		error_msg_and_die("scandir failed: %s", name);
 	}
 
 	/* process directory */
@@ -269,25 +411,20 @@
 		}
 		namelen = strlen(dirent->d_name);
 		if (namelen > MAX_INPUT_NAMELEN) {
-			die(MKFS_ERROR, 0,
-				"very long (%u bytes) filename found: %s\n"
-				"please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile",
+			error_msg_and_die(
+				"Very long (%u bytes) filename `%s' found.\n"
+				" Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile.  Exiting.\n",
 				namelen, dirent->d_name);
 		}
 		memcpy(endpath, dirent->d_name, namelen + 1);
 
 		if (lstat(path, &st) < 0) {
+			perror(endpath);
 			warn_skip = 1;
 			continue;
 		}
-		entry = calloc(1, sizeof(struct entry));
-		if (!entry) {
-			die(MKFS_ERROR, 1, "calloc failed");
-		}
-		entry->name = strdup(dirent->d_name);
-		if (!entry->name) {
-			die(MKFS_ERROR, 1, "strdup failed");
-		}
+		entry = xcalloc(1, sizeof(struct entry));
+		entry->name = xstrdup(dirent->d_name);
 		/* truncate multi-byte UTF-8 filenames on character boundary */
 		if (namelen > CRAMFS_MAXPATHLEN) {
 			namelen = CRAMFS_MAXPATHLEN;
@@ -297,24 +434,25 @@
 				namelen--;
 				/* are we reasonably certain it was UTF-8 ? */
 				if (entry->name[namelen] < 0x80 || !namelen) {
-					die(MKFS_ERROR, 0, "cannot truncate filenames not encoded in UTF-8");
+					error_msg_and_die("cannot truncate filenames not encoded in UTF-8");
 				}
 			}
 			entry->name[namelen] = '\0';
 		}
 		entry->mode = st.st_mode;
 		entry->size = st.st_size;
-		entry->uid = st.st_uid;
+		entry->uid = opt_squash ? 0 : st.st_uid;
 		if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
 			warn_uid = 1;
-		entry->gid = st.st_gid;
-		if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
+		entry->gid = opt_squash ? 0 : st.st_gid;
+		if (entry->gid >= 1 << CRAMFS_GID_WIDTH) {
 			/* TODO: We ought to replace with a default
 			   gid instead of truncating; otherwise there
 			   are security problems.  Maybe mode should
 			   be &= ~070.  Same goes for uid once Linux
 			   supports >16-bit uids. */
 			warn_gid = 1;
+		}
 		size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
 		*fslen_ub += size;
 		if (S_ISDIR(st.st_mode)) {
@@ -325,21 +463,15 @@
 					warn_skip = 1;
 					continue;
 				}
-				entry->path = strdup(path);
-				if (!entry->path) {
-					die(MKFS_ERROR, 1, "strdup failed");
-				}
+				entry->path = xstrdup(path);
 				if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) {
 					warn_size = 1;
 					entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
 				}
 			}
 		} else if (S_ISLNK(st.st_mode)) {
-			entry->uncompressed = malloc(entry->size);
+			entry->uncompressed = xreadlink(path);
 			if (!entry->uncompressed) {
-				die(MKFS_ERROR, 1, "malloc failed");
-			}
-			if (readlink(path, entry->uncompressed, entry->size) < 0) {
 				warn_skip = 1;
 				continue;
 			}
@@ -351,7 +483,7 @@
 			if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
 				warn_dev = 1;
 		} else {
-			die(MKFS_ERROR, 0, "bogus file type: %s", entry->name);
+			error_msg_and_die("bogus file type: %s", entry->name);
 		}
 
 		if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
@@ -378,7 +510,9 @@
 	struct cramfs_super *super = (struct cramfs_super *) base;
 	unsigned int offset = sizeof(struct cramfs_super) + image_length;
 
-	offset += opt_pad;	/* 0 if no padding */
+	if (opt_pad) {
+		offset += opt_pad;	/* 0 if no padding */
+	}
 
 	super->magic = CRAMFS_MAGIC;
 	super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
@@ -414,10 +548,10 @@
 	struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
 
 	if ((offset & 3) != 0) {
-		die(MKFS_ERROR, 0, "illegal offset of %lu bytes", offset);
+		error_msg_and_die("illegal offset of %lu bytes", offset);
 	}
 	if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
-		die(MKFS_ERROR, 0, "filesystem too big");
+		error_msg_and_die("filesystem too big");
 	}
 	inode->offset = (offset >> 2);
 }
@@ -429,7 +563,7 @@
  */
 static void print_node(struct entry *e)
 {
-	char info[10];
+	char info[12];
 	char type = '?';
 
 	if (S_ISREG(e->mode)) type = 'f';
@@ -442,11 +576,11 @@
 
 	if (S_ISCHR(e->mode) || (S_ISBLK(e->mode))) {
 		/* major/minor numbers can be as high as 2^12 or 4096 */
-		snprintf(info, 10, "%4d,%4d", major(e->size), minor(e->size));
+		snprintf(info, 11, "%4d,%4d", major(e->size), minor(e->size));
 	}
 	else {
 		/* size be as high as 2^24 or 16777216 */
-		snprintf(info, 10, "%9d", e->size);
+		snprintf(info, 11, "%9d", e->size);
 	}
 
 	printf("%c %04o %s %5d:%-3d %s\n",
@@ -462,17 +596,9 @@
 {
 	int stack_entries = 0;
 	int stack_size = 64;
-	struct entry **entry_stack;
-
-	entry_stack = malloc(stack_size * sizeof(struct entry *));
-	if (!entry_stack) {
-		die(MKFS_ERROR, 1, "malloc failed");
-	}
-
-	if (opt_verbose) {
-		printf("root:\n");
-	}
+	struct entry **entry_stack = NULL;
 
+	entry_stack = xmalloc(stack_size * sizeof(struct entry *));
 	for (;;) {
 		int dir_start = stack_entries;
 		while (entry) {
@@ -506,10 +632,7 @@
 			if (entry->child) {
 				if (stack_entries >= stack_size) {
 					stack_size *= 2;
-					entry_stack = realloc(entry_stack, stack_size * sizeof(struct entry *));
-					if (!entry_stack) {
-						die(MKFS_ERROR, 1, "realloc failed");
-					}
+					entry_stack = xrealloc(entry_stack, stack_size * sizeof(struct entry *));
 				}
 				entry_stack[stack_entries] = entry;
 				stack_entries++;
@@ -543,7 +666,7 @@
 
 		set_data_offset(entry, base, offset);
 		if (opt_verbose) {
-			printf("%s:\n", entry->name);
+		    printf("'%s':\n", entry->name);
 		}
 		entry = entry->child;
 	}
@@ -553,16 +676,21 @@
 
 static int is_zero(char const *begin, unsigned len)
 {
-	/* Returns non-zero iff the first LEN bytes from BEGIN are all NULs. */
-	return (len-- == 0 ||
-		(begin[0] == '\0' &&
-		 (len-- == 0 ||
-		  (begin[1] == '\0' &&
-		   (len-- == 0 ||
-		    (begin[2] == '\0' &&
-		     (len-- == 0 ||
-		      (begin[3] == '\0' &&
-		       memcmp(begin, begin + 4, len) == 0))))))));
+	if (opt_holes)
+		/* Returns non-zero iff the first LEN bytes from BEGIN are
+		   all NULs. */
+		return (len-- == 0 ||
+			(begin[0] == '\0' &&
+			 (len-- == 0 ||
+			  (begin[1] == '\0' &&
+			   (len-- == 0 ||
+			    (begin[2] == '\0' &&
+			     (len-- == 0 ||
+			      (begin[3] == '\0' &&
+			       memcmp(begin, begin + 4, len) == 0))))))));
+	else
+		/* Never create holes. */
+		return 0;
 }
 
 /*
@@ -575,37 +703,34 @@
  * Note that size > 0, as a zero-sized file wouldn't ever
  * have gotten here in the first place.
  */
-static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size)
+static unsigned int do_compress(char *base, unsigned int offset, struct entry *entry)
 {
+	unsigned int size = entry->size;
 	unsigned long original_size = size;
 	unsigned long original_offset = offset;
 	unsigned long new_size;
 	unsigned long blocks = (size - 1) / blksize + 1;
 	unsigned long curr = offset + 4 * blocks;
 	int change;
+	char *uncompressed = entry->uncompressed;
 
-	total_blocks += blocks;
+	total_blocks += blocks; 
 
 	do {
 		unsigned long len = 2 * blksize;
 		unsigned int input = size;
-		int err;
-
 		if (input > blksize)
 			input = blksize;
 		size -= input;
-		if (!(opt_holes && is_zero (uncompressed, input))) {
-			err = compress2(base + curr, &len, uncompressed, input, Z_BEST_COMPRESSION);
-			if (err != Z_OK) {
-				die(MKFS_ERROR, 0, "compression error: %s", zError(err));
-			}
+		if (!is_zero (uncompressed, input)) {
+			compress(base + curr, &len, uncompressed, input);
 			curr += len;
 		}
 		uncompressed += input;
 
 		if (len > blksize*2) {
 			/* (I don't think this can happen with zlib.) */
-			die(MKFS_ERROR, 0, "AIEEE: block \"compressed\" to > 2*blocklength (%ld)", len);
+			error_msg_and_die("AIEEE: block \"compressed\" to > 2*blocklength (%ld)\n", len);
 		}
 
 		*(u32 *) (base + offset) = curr;
@@ -618,10 +743,12 @@
 	   st_blocks * 512.  But if you say that then perhaps
 	   administrative data should also be included in both. */
 	change = new_size - original_size;
-	if (opt_verbose > 1) {
-		printf("%6.2f%% (%+d bytes)\t%s\n",
-		       (change * 100) / (double) original_size, change, name);
+#if 0
+	if (opt_verbose) {
+	    printf("%6.2f%% (%+d bytes)\t%s\n",
+		    (change * 100) / (double) original_size, change, entry->name);
 	}
+#endif
 
 	return curr;
 }
@@ -644,7 +771,7 @@
 				set_data_offset(entry, base, offset);
 				entry->offset = offset;
 				map_entry(entry);
-				offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size);
+				offset = do_compress(base, offset, entry);
 				unmap_entry(entry);
 			}
 		}
@@ -660,13 +787,10 @@
 	int fd;
 	char *buf;
 
-	fd = open(file, O_RDONLY);
-	if (fd < 0) {
-		die(MKFS_ERROR, 1, "open failed: %s", file);
-	}
+	fd = xopen(file, O_RDONLY, 0);
 	buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
 	if (buf == MAP_FAILED) {
-		die(MKFS_ERROR, 1, "mmap failed");
+		error_msg_and_die("mmap failed");
 	}
 	memcpy(base + offset, buf, image_length);
 	munmap(buf, image_length);
@@ -679,6 +803,328 @@
 	return (offset + image_length);
 }
 
+static struct entry *find_filesystem_entry(struct entry *dir, char *name, mode_t type)
+{
+	struct entry *e = dir;
+
+	if (S_ISDIR(dir->mode)) {
+		e = dir->child;
+	}
+	while (e) {
+		/* Only bother to do the expensive strcmp on matching file types */
+		if (type == (e->mode & S_IFMT) && e->name) {
+			if (S_ISDIR(e->mode)) {
+				int len = strlen(e->name);
+
+				/* Check if we are a parent of the correct path */
+				if (strncmp(e->name, name, len) == 0) {
+					/* Is this an _exact_ match? */
+					if (strcmp(name, e->name) == 0) {
+						return (e);
+					}
+					/* Looks like we found a parent of the correct path */
+					if (name[len] == '/') {
+						if (e->child) {
+							return (find_filesystem_entry (e, name + len + 1, type));
+						} else {
+							return NULL;
+						}
+					}
+				}
+			} else {
+				if (strcmp(name, e->name) == 0) {
+					return (e);
+				}
+			}
+		}
+		e = e->next;
+	}
+	return (NULL);
+}
+
+void modify_entry(char *full_path, unsigned long uid, unsigned long gid, 
+	unsigned long mode, unsigned long rdev, struct entry *root, loff_t *fslen_ub)
+{
+	char *name, *path, *full;
+	struct entry *curr, *parent, *entry, *prev;
+	
+	full = xstrdup(full_path);
+	path = xstrdup(dirname(full));
+	name = full_path + strlen(path) + 1;
+	free(full);
+	if (strcmp(path, "/") == 0) {
+		parent = root;
+		name = full_path + 1;
+	} else {
+		if (!(parent = find_filesystem_entry(root, path+1, S_IFDIR)))
+			error_msg_and_die("%s/%s: could not find parent\n", path, name);
+	}
+	if ((entry = find_filesystem_entry(parent, name, (mode & S_IFMT)))) {
+		/* its there, just modify permissions */
+		entry->mode = mode;
+		entry->uid = uid;
+		entry->gid = gid;
+	} else { /* make a new entry */
+	
+		/* code partially replicated from parse_directory() */
+		size_t namelen;
+		if (S_ISREG(mode)) {
+			error_msg_and_die("%s: regular file from device_table file must exist on disk!", full_path);
+		}
+
+		namelen = strlen(name);
+		if (namelen > MAX_INPUT_NAMELEN) {
+			error_msg_and_die(
+				"Very long (%u bytes) filename `%s' found.\n"
+				" Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile.  Exiting.\n",
+				namelen, name);
+		}
+		entry = xcalloc(1, sizeof(struct entry));
+		entry->name = xstrdup(name);
+		/* truncate multi-byte UTF-8 filenames on character boundary */
+		if (namelen > CRAMFS_MAXPATHLEN) {
+			namelen = CRAMFS_MAXPATHLEN;
+			warn_namelen = 1;
+			/* the first lost byte must not be a trail byte */
+			while ((entry->name[namelen] & 0xc0) == 0x80) {
+				namelen--;
+				/* are we reasonably certain it was UTF-8 ? */
+				if (entry->name[namelen] < 0x80 || !namelen) {
+					error_msg_and_die("cannot truncate filenames not encoded in UTF-8");
+				}
+			}
+			entry->name[namelen] = '\0';
+		}
+		entry->mode = mode;
+		entry->uid = uid;
+		entry->gid = gid;
+		entry->size = 0;
+		if (S_ISBLK(mode) || S_ISCHR(mode)) {
+			entry->size = rdev;
+			if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
+				warn_dev = 1;
+		}
+		
+		/* ok, now we have to backup and correct the size of all the entries above us */
+		*fslen_ub += sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
+		parent->size += sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
+
+		/* alright, time to link us in */
+		curr = parent->child;
+		prev = NULL;
+		while (curr && strcmp(name, curr->name) > 0) {
+			prev = curr;
+			curr = curr->next;
+		}
+		if (!prev) parent->child = entry;
+		else prev->next = entry;
+		entry->next = curr;
+		entry->child = NULL;
+	}
+	if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
+		warn_uid = 1;
+	if (entry->gid >= 1 << CRAMFS_GID_WIDTH) {
+		/* TODO: We ought to replace with a default
+		   gid instead of truncating; otherwise there
+		   are security problems.  Maybe mode should
+		   be &= ~070.  Same goes for uid once Linux
+		   supports >16-bit uids. */
+		warn_gid = 1;
+	}
+	free(path);
+}
+
+/* the GNU C library has a wonderful scanf("%as", string) which will
+ allocate the string with the right size, good to avoid buffer overruns. 
+ the following macros use it if available or use a hacky workaround...
+ */
+
+#ifdef __GNUC__
+#define SCANF_PREFIX "a"
+#define SCANF_STRING(s) (&s)
+#define GETCWD_SIZE 0
+#else
+#define SCANF_PREFIX "511"
+#define SCANF_STRING(s) (s = xmalloc(512))
+#define GETCWD_SIZE -1
+inline int snprintf(char *str, size_t n, const char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = vsprintf(str, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+#endif
+
+/*  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(char *line, struct entry *root, loff_t *fslen_ub)
+{
+	char type, *name = NULL;
+	unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
+	unsigned long start = 0, increment = 1, count = 0;
+
+	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");
+	}
+
+	switch (type) {
+	case 'd':
+		mode |= S_IFDIR;
+		modify_entry(name, uid, gid, mode, 0, root, fslen_ub);
+		break;
+	case 'f':
+		mode |= S_IFREG;
+		modify_entry(name, uid, gid, mode, 0, root, fslen_ub);
+		break;
+	case 'p':
+		mode |= S_IFIFO;
+		modify_entry(name, uid, gid, mode, 0, root, fslen_ub);
+		break;
+	case 'c':
+	case 'b':
+		mode |= (type == 'c') ? S_IFCHR : S_IFBLK;
+		if (count > 0) {
+			char *buf;
+			unsigned long i;
+			dev_t rdev;
+
+			for (i = start; i < count; i++) {
+				asprintf(&buf, "%s%lu", name, i);
+				rdev = makedev(major, minor + (i * increment - start));
+				modify_entry(buf, uid, gid, mode, rdev, root, fslen_ub);
+				free(buf);
+			}
+		} else {
+			dev_t rdev = makedev(major, minor);
+			modify_entry(name, uid, gid, mode, rdev, root, fslen_ub);
+		}
+		break;
+	default:
+		error_msg_and_die("Unsupported file type");
+	}
+	free(name);
+	return 0;
+}
+
+static int parse_device_table(FILE *file, struct entry *root, loff_t *fslen_ub)
+{
+	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 */
+	opt_squash = 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(line, root, fslen_ub))
+				status = 1;
+		}
+
+		free(line);
+		line = NULL;
+	}
+	free(line);
+	fclose(file);
+
+	return status;
+}
+
+void traverse(struct entry *entry, int depth)
+{
+	struct entry *curr = entry;
+	int i;
+
+	while (curr) {
+		for (i = 0; i < depth; i++) putchar(' ');
+		printf("%s: size=%d mode=%d same=%p\n",
+			(curr->name)? (char*)curr->name : "/", 
+			curr->size, curr->mode, curr->same);
+		if (curr->child) traverse(curr->child, depth + 4);
+		curr = curr->next;
+	}
+}
+
+static void free_filesystem_entry(struct entry *dir)
+{
+	struct entry *e = dir, *last;
+
+	if (S_ISDIR(dir->mode)) {
+		e = dir->child;
+	}
+	while (e) {
+		if (e->name)
+			free(e->name);
+		if (e->path)
+			free(e->path);
+		if (e->uncompressed)
+			free(e->uncompressed);
+		last = e;
+		if (e->child) {
+			free_filesystem_entry(e);
+		}
+		e = e->next;
+		free(last);
+	}
+}
+
+
+/*
+ * Usage:
+ *
+ *      mkcramfs directory-name outfile
+ *
+ * where "directory-name" is simply the root of the directory
+ * tree that we want to generate a compressed filesystem out
+ * of.
+ */
 int main(int argc, char **argv)
 {
 	struct stat st;		/* used twice... */
@@ -692,6 +1138,7 @@
 	u32 crc;
 	int c;			/* for getopt */
 	char *ep;		/* for strtoul */
+	FILE *devtable = NULL;
 
 	total_blocks = 0;
 
@@ -699,7 +1146,7 @@
 		progname = argv[0];
 
 	/* command line options */
-	while ((c = getopt(argc, argv, "hEe:i:n:psvz")) != EOF) {
+	while ((c = getopt(argc, argv, "hEe:i:n:psvzD:q")) != EOF) {
 		switch (c) {
 		case 'h':
 			usage(MKFS_OK);
@@ -715,7 +1162,7 @@
 		case 'i':
 			opt_image = optarg;
 			if (lstat(opt_image, &st) < 0) {
-				die(MKFS_ERROR, 1, "lstat failed: %s", opt_image);
+				error_msg_and_die("lstat failed: %s", opt_image);
 			}
 			image_length = st.st_size; /* may be padded later */
 			fslen_ub += (image_length + 3); /* 3 is for padding */
@@ -736,6 +1183,16 @@
 		case 'z':
 			opt_holes = 1;
 			break;
+		case 'q':
+			opt_squash = 1;
+			break;
+		case 'D':
+			devtable = xfopen(optarg, "r");
+			if (fstat(fileno(devtable), &st) < 0)
+				perror_msg_and_die(optarg);
+			if (st.st_size < 10)
+				error_msg_and_die("%s: not a proper device table file\n", optarg);
+			break;
 		}
 	}
 
@@ -745,25 +1202,23 @@
 	outfile = argv[optind + 1];
 
 	if (stat(dirname, &st) < 0) {
-		die(MKFS_USAGE, 1, "stat failed: %s", dirname);
-	}
-	fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-	if (fd < 0) {
-		die(MKFS_USAGE, 1, "open failed: %s", outfile);
+		error_msg_and_die("stat failed: %s", dirname);
 	}
+	fd = xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
 
-	root_entry = calloc(1, sizeof(struct entry));
-	if (!root_entry) {
-		die(MKFS_ERROR, 1, "calloc failed");
-	}
+	root_entry = xcalloc(1, sizeof(struct entry));
 	root_entry->mode = st.st_mode;
 	root_entry->uid = st.st_uid;
 	root_entry->gid = st.st_gid;
 
 	root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub);
 
+	if (devtable) {
+		parse_device_table(devtable, root_entry, &fslen_ub);
+	}
+
 	/* always allocate a multiple of blksize bytes because that's
-	   what we're going to write later on */
+           what we're going to write later on */
 	fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
 
 	if (fslen_ub > MAXFSLEN) {
@@ -790,7 +1245,7 @@
 	rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 
 	if (rom_image == MAP_FAILED) {
-		die(MKFS_ERROR, 1, "mmap failed");
+		error_msg_and_die("mmap failed");
 	}
 
 	/* Skip the first opt_pad bytes for boot loader code */
@@ -807,6 +1262,7 @@
 	}
 
 	offset = write_directory_structure(root_entry->child, rom_image, offset);
+	if (opt_verbose)
 	printf("Directory data: %d bytes\n", offset);
 
 	offset = write_data(root_entry, rom_image, offset);
@@ -814,30 +1270,38 @@
 	/* We always write a multiple of blksize bytes, so that
 	   losetup works. */
 	offset = ((offset - 1) | (blksize - 1)) + 1;
+	if (opt_verbose)
 	printf("Everything: %d kilobytes\n", offset >> 10);
 
 	/* Write the superblock now that we can fill in all of the fields. */
 	write_superblock(root_entry, rom_image+opt_pad, offset);
+	if (opt_verbose)
 	printf("Super block: %d bytes\n", sizeof(struct cramfs_super));
 
 	/* Put the checksum in. */
 	crc = crc32(0L, Z_NULL, 0);
 	crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad));
 	((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc;
+	if (opt_verbose)
 	printf("CRC: %x\n", crc);
 
 	/* Check to make sure we allocated enough space. */
 	if (fslen_ub < offset) {
-		die(MKFS_ERROR, 0, "not enough space allocated for ROM image (%Ld allocated, %d used)", fslen_ub, offset);
+		error_msg_and_die("not enough space allocated for ROM "
+			"image (%Ld allocated, %d used)", fslen_ub, offset);
 	}
 
 	written = write(fd, rom_image, offset);
 	if (written < 0) {
-		die(MKFS_ERROR, 1, "write failed");
+		error_msg_and_die("write failed");
 	}
 	if (offset != written) {
-		die(MKFS_ERROR, 0, "ROM image write failed (wrote %d of %d bytes)", written, offset);
+		error_msg_and_die("ROM image write failed (wrote %d of %d bytes)", written, offset);
 	}
+	
+	/* Free up memory */
+	free_filesystem_entry(root_entry);
+	free(root_entry);
 
 	/* (These warnings used to come at the start, but they scroll off the
 	   screen too quickly.) */