Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
Index: jffsX-utils/mkfs.jffs2.c
===================================================================
--- jffsX-utils/mkfs.jffs2.c	(nonexistent)
+++ jffsX-utils/mkfs.jffs2.c	(revision 5)
@@ -0,0 +1,1850 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Build a JFFS2 image in a file, from a given directory tree.
+ *
+ * Copyright 2001, 2002 Red Hat, Inc.
+ *           2001 David A. Schleef <ds@lineo.com>
+ *           2002 Axis Communications AB
+ *           2001, 2002 Erik Andersen <andersen@codepoet.org>
+ *           2004 University of Szeged, Hungary
+ *           2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ *
+ * Cross-endian support added by David Schleef <ds@schleef.org>.
+ *
+ * Major architectural rewrite by Erik Andersen <andersen@codepoet.org>
+ * to allow support for making hard links (though hard links support is
+ * not yet implemented), and for munging file permissions and ownership
+ * on the fly using --faketime, --squash, --devtable.   And I plugged a
+ * few memory leaks, adjusted the error handling and fixed some little
+ * nits here and there.
+ *
+ * I also added a sample device table file.  See device_table.txt
+ *  -Erik, September 2001
+ *
+ * Cleanmarkers support added by Axis Communications AB
+ *
+ * Rewritten again.  Cleanly separated host and target filsystem
+ * activities (mainly so I can reuse all the host handling stuff as I
+ * rewrite other mkfs utils).  Added a verbose option to list types
+ * and attributes as files are added to the file system.  Major cleanup
+ * and scrubbing of the code so it can be read, understood, and
+ * modified by mere mortals.
+ *
+ *  -Erik, November 2002
+ */
+
+#define PROGRAM_NAME "mkfs.jffs2"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#ifndef WITHOUT_XATTR
+#include <sys/xattr.h>
+#include <sys/acl.h>
+#endif
+#include <byteswap.h>
+#include <crc32.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include "rbtree.h"
+#include "common.h"
+
+/* Do not use the weird XPG version of basename */
+#undef basename
+
+//#define DMALLOC
+//#define mkfs_debug_msg    errmsg
+#define mkfs_debug_msg(a...)	{ }
+
+#define PAD(x) (((x)+3)&~3)
+
+struct filesystem_entry {
+	char *name;					/* Name of this directory (think basename) */
+	char *path;					/* Path of this directory (think dirname) */
+	char *fullname;				/* Full name of this directory (i.e. path+name) */
+	char *hostname;				/* Full path to this file on the host filesystem */
+	uint32_t ino;				/* Inode number of this file in JFFS2 */
+	struct stat sb;				/* Stores directory permissions and whatnot */
+	char *link;					/* Target a symlink points to. */
+	struct filesystem_entry *parent;	/* Parent directory */
+	struct filesystem_entry *prev;	/* Only relevant to non-directories */
+	struct filesystem_entry *next;	/* Only relevant to non-directories */
+	struct filesystem_entry *files;	/* Only relevant to directories */
+	struct rb_node hardlink_rb;
+};
+
+struct ignorepath_entry {
+	struct ignorepath_entry* next;  /* Points to the next ignorepath element */
+	char name[PATH_MAX];        /* Name of the entry */
+};
+static struct ignorepath_entry* ignorepath = 0;
+struct rb_root hardlinks;
+static int out_fd = -1;
+static int in_fd = -1;
+static char default_rootdir[] = ".";
+static char *rootdir = default_rootdir;
+static int verbose = 0;
+static int squash_uids = 0;
+static int squash_perms = 0;
+static int fake_times = 0;
+int target_endian = __BYTE_ORDER;
+
+static uint32_t find_hardlink(struct filesystem_entry *e)
+{
+	struct filesystem_entry *f;
+	struct rb_node **n = &hardlinks.rb_node;
+	struct rb_node *parent = NULL;
+
+	while (*n) {
+		parent = *n;
+		f = rb_entry(parent, struct filesystem_entry, hardlink_rb);
+
+		if ((f->sb.st_dev < e->sb.st_dev) ||
+		    (f->sb.st_dev == e->sb.st_dev &&
+		     f->sb.st_ino < e->sb.st_ino))
+			n = &parent->rb_left;
+		else if ((f->sb.st_dev > e->sb.st_dev) ||
+			 (f->sb.st_dev == e->sb.st_dev &&
+			  f->sb.st_ino > e->sb.st_ino)) {
+			n = &parent->rb_right;
+		} else
+			return f->ino;
+	}
+
+	rb_link_node(&e->hardlink_rb, parent, n);
+	rb_insert_color(&e->hardlink_rb, &hardlinks);
+	return 0;
+}
+
+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) {
+			sys_errmsg("%s:%s", PROGRAM_NAME, path);
+			free(buf);
+			return NULL;
+		}
+	}
+	while (bufsize < readsize + 1);
+
+	buf[readsize] = '\0';
+
+	return buf;
+}
+static FILE *xfopen(const char *path, const char *mode)
+{
+	FILE *fp;
+	if ((fp = fopen(path, mode)) == NULL)
+		sys_errmsg_die("%s", path);
+	return fp;
+}
+
+static struct filesystem_entry *find_filesystem_entry(
+		struct filesystem_entry *dir, char *fullname, uint32_t type)
+{
+	struct filesystem_entry *e = dir;
+
+	if (S_ISDIR(dir->sb.st_mode)) {
+		/* If this is the first call, and we actually want this
+		 * directory, then return it now */
+		if (strcmp(fullname, e->fullname) == 0)
+			return e;
+
+		e = dir->files;
+	}
+	while (e) {
+		if (S_ISDIR(e->sb.st_mode)) {
+			int len = strlen(e->fullname);
+
+			/* Check if we are a parent of the correct path */
+			if (strncmp(e->fullname, fullname, len) == 0) {
+				/* Is this an _exact_ match? */
+				if (strcmp(fullname, e->fullname) == 0) {
+					return (e);
+				}
+				/* Looks like we found a parent of the correct path */
+				if (fullname[len] == '/') {
+					if (e->files) {
+						return (find_filesystem_entry (e, fullname, type));
+					} else {
+						return NULL;
+					}
+				}
+			}
+		} else {
+			if (strcmp(fullname, e->fullname) == 0) {
+				return (e);
+			}
+		}
+		e = e->next;
+	}
+	return (NULL);
+}
+
+static struct filesystem_entry *add_host_filesystem_entry(const char *name,
+		const char *path, unsigned long uid, unsigned long gid,
+		unsigned long mode, dev_t rdev, struct filesystem_entry *parent)
+{
+	int status;
+	char *tmp;
+	struct stat sb;
+	time_t timestamp = time(NULL);
+	struct filesystem_entry *entry;
+
+	memset(&sb, 0, sizeof(struct stat));
+	status = lstat(path, &sb);
+
+	if (status >= 0) {
+		/* It is ok for some types of files to not exit on disk (such as
+		 * device nodes), but if they _do_ exist the specified mode had
+		 * better match the actual file or strange things will happen.... */
+		if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) {
+			errmsg_die ("%s: file type does not match specified type!", path);
+		}
+		timestamp = sb.st_mtime;
+	} else {
+		/* If this is a regular file, it _must_ exist on disk */
+		if ((mode & S_IFMT) == S_IFREG) {
+			errmsg_die("%s: does not exist!", path);
+		}
+	}
+
+	/* Squash all permissions so files are owned by root, all
+	 * timestamps are _right now_, and file permissions
+	 * have group and other write removed */
+	if (squash_uids) {
+		uid = gid = 0;
+	}
+	if (squash_perms) {
+		if (!S_ISLNK(mode)) {
+			mode &= ~(S_IWGRP | S_IWOTH);
+			mode &= ~(S_ISUID | S_ISGID);
+		}
+	}
+	if (fake_times) {
+		timestamp = 0;
+	}
+
+	entry = xcalloc(1, sizeof(struct filesystem_entry));
+
+	entry->hostname = xstrdup(path);
+	entry->fullname = xstrdup(name);
+	tmp = xstrdup(name);
+	entry->name = xstrdup(basename(tmp));
+	free(tmp);
+	tmp = xstrdup(name);
+	entry->path = xstrdup(dirname(tmp));
+	free(tmp);
+
+	entry->sb.st_ino = sb.st_ino;
+	entry->sb.st_dev = sb.st_dev;
+	entry->sb.st_nlink = sb.st_nlink;
+
+	entry->sb.st_uid = uid;
+	entry->sb.st_gid = gid;
+	entry->sb.st_mode = mode;
+	entry->sb.st_rdev = rdev;
+	entry->sb.st_atime = entry->sb.st_ctime =
+		entry->sb.st_mtime = timestamp;
+	if (S_ISREG(mode)) {
+		entry->sb.st_size = sb.st_size;
+	}
+	if (S_ISLNK(mode)) {
+		entry->link = xreadlink(path);
+		entry->sb.st_size = strlen(entry->link);
+	}
+
+	/* This happens only for root */
+	if (!parent)
+		return (entry);
+
+	/* Hook the file into the parent directory */
+	entry->parent = parent;
+	if (!parent->files) {
+		parent->files = entry;
+	} else {
+		struct filesystem_entry *prev;
+		for (prev = parent->files; prev->next; prev = prev->next);
+		prev->next = entry;
+		entry->prev = prev;
+	}
+
+	return (entry);
+}
+
+static struct filesystem_entry *recursive_add_host_directory(
+		struct filesystem_entry *parent, const char *targetpath,
+		const char *hostpath)
+{
+	int i, n;
+	struct stat sb;
+	char *hpath, *tpath;
+	struct dirent *dp, **namelist;
+	struct filesystem_entry *entry;
+	struct ignorepath_entry* element = ignorepath;
+
+	if (lstat(hostpath, &sb)) {
+		sys_errmsg_die("%s", hostpath);
+	}
+
+	entry = add_host_filesystem_entry(targetpath, hostpath,
+			sb.st_uid, sb.st_gid, sb.st_mode, 0, parent);
+
+	while ( element ) {
+		if ( strcmp( element->name, targetpath ) == 0 ) {
+			printf( "Note: ignoring directories below '%s'\n", targetpath );
+			return entry;
+			break;
+		}
+		element = element->next;
+	}
+
+	n = scandir(hostpath, &namelist, 0, alphasort);
+	if (n < 0) {
+		sys_errmsg_die("opening directory %s", hostpath);
+	}
+
+	for (i=0; i<n; i++)
+	{
+		dp = namelist[i];
+		if (dp->d_name[0] == '.' && (dp->d_name[1] == 0 ||
+					(dp->d_name[1] == '.' &&  dp->d_name[2] == 0)))
+		{
+			free(dp);
+			continue;
+		}
+
+		xasprintf(&hpath, "%s/%s", hostpath, dp->d_name);
+		if (lstat(hpath, &sb)) {
+			sys_errmsg_die("%s", hpath);
+		}
+		if (strcmp(targetpath, "/") == 0) {
+			xasprintf(&tpath, "%s%s", targetpath, dp->d_name);
+		} else {
+			xasprintf(&tpath, "%s/%s", targetpath, dp->d_name);
+		}
+
+		switch (sb.st_mode & S_IFMT) {
+			case S_IFDIR:
+				recursive_add_host_directory(entry, tpath, hpath);
+				break;
+
+			case S_IFREG:
+			case S_IFSOCK:
+			case S_IFIFO:
+			case S_IFLNK:
+			case S_IFCHR:
+			case S_IFBLK:
+				add_host_filesystem_entry(tpath, hpath, sb.st_uid,
+						sb.st_gid, sb.st_mode, sb.st_rdev, entry);
+				break;
+
+			default:
+				errmsg("Unknown file type %o for %s", sb.st_mode, hpath);
+				break;
+		}
+		free(dp);
+		free(hpath);
+		free(tpath);
+	}
+	free(namelist);
+	return (entry);
+}
+
+/* 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__
+#if __STDC_VERSION__ >= 199901L
+#define SCANF_PREFIX "m"
+#else
+#define SCANF_PREFIX "a"
+#endif
+#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(struct filesystem_entry *root, char *line)
+{
+	char *hostpath;
+	char type, *name = NULL, *tmp, *dir;
+	unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
+	unsigned long start = 0, increment = 1, count = 0;
+	struct filesystem_entry *parent, *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, "/")) {
+		errmsg_die("Device table entries require absolute paths");
+	}
+
+	xasprintf(&hostpath, "%s%s", rootdir, name);
+
+	/* 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;
+		case 'l':
+			mode |= S_IFLNK;
+			break;
+		default:
+			errmsg_die("Unsupported file type '%c'", type);
+	}
+	entry = find_filesystem_entry(root, name, mode);
+	if (entry && !(count > 0 && (type == 'c' || type == 'b'))) {
+		/* Ok, we just need to fixup the existing entry
+		 * and we will be all done... */
+		entry->sb.st_uid = uid;
+		entry->sb.st_gid = gid;
+		entry->sb.st_mode = mode;
+		if (major && minor) {
+			entry->sb.st_rdev = makedev(major, minor);
+		}
+	} else {
+		/* If parent is NULL (happens with device table entries),
+		 * try and find our parent now) */
+		tmp = xstrdup(name);
+		dir = dirname(tmp);
+		parent = find_filesystem_entry(root, dir, S_IFDIR);
+		free(tmp);
+		if (parent == NULL) {
+			errmsg ("skipping device_table entry '%s': no parent directory!", name);
+			free(name);
+			free(hostpath);
+			return 1;
+		}
+
+		switch (type) {
+			case 'd':
+				add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+				break;
+			case 'f':
+				add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+				break;
+			case 'p':
+				add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+				break;
+			case 'c':
+			case 'b':
+				if (count > 0) {
+					dev_t rdev;
+					unsigned long i;
+					char *dname, *hpath;
+
+					for (i = start; i < (start + count); i++) {
+						xasprintf(&dname, "%s%lu", name, i);
+						xasprintf(&hpath, "%s/%s%lu", rootdir, name, i);
+						rdev = makedev(major, minor + (i - start) * increment);
+						add_host_filesystem_entry(dname, hpath, uid, gid,
+								mode, rdev, parent);
+						free(dname);
+						free(hpath);
+					}
+				} else {
+					dev_t rdev = makedev(major, minor);
+					add_host_filesystem_entry(name, hostpath, uid, gid,
+							mode, rdev, parent);
+				}
+				break;
+			default:
+				errmsg_die("Unsupported file type '%c'", type);
+		}
+	}
+	free(name);
+	free(hostpath);
+	return 0;
+}
+
+static int parse_device_table(struct filesystem_entry *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;
+}
+
+static void cleanup(struct filesystem_entry *dir)
+{
+	struct filesystem_entry *e, *prev;
+
+	e = dir->files;
+	while (e) {
+		if (e->name)
+			free(e->name);
+		if (e->path)
+			free(e->path);
+		if (e->fullname)
+			free(e->fullname);
+		e->next = NULL;
+		e->name = NULL;
+		e->path = NULL;
+		e->fullname = NULL;
+		e->prev = NULL;
+		prev = e;
+		if (S_ISDIR(e->sb.st_mode)) {
+			cleanup(e);
+		}
+		e = e->next;
+		free(prev);
+	}
+}
+
+/* Here is where we do the actual creation of the file system */
+#include "mtd/jffs2-user.h"
+
+#define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF
+#ifndef JFFS2_MAX_SYMLINK_LEN
+#define JFFS2_MAX_SYMLINK_LEN 254
+#endif
+
+static uint32_t ino = 0;
+static uint8_t *file_buffer = NULL;		/* file buffer contains the actual erase block*/
+static int out_ofs = 0;
+static int erase_block_size = 65536;
+static int pad_fs_size = 0;
+static int add_cleanmarkers = 1;
+static struct jffs2_unknown_node cleanmarker;
+static int cleanmarker_size = sizeof(cleanmarker);
+static unsigned char ffbuf[16] =
+{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/* We set this at start of main() using sysconf(), -1 means we don't know */
+/* When building an fs for non-native systems, use --pagesize=SIZE option */
+int page_size = -1;
+
+#include "compr.h"
+
+static void full_write(int fd, const void *buf, int len)
+{
+	int ret;
+
+	while (len > 0) {
+		ret = write(fd, buf, len);
+
+		if (ret < 0)
+			sys_errmsg_die("write");
+
+		if (ret == 0)
+			sys_errmsg_die("write returned zero");
+
+		len -= ret;
+		buf += ret;
+		out_ofs += ret;
+	}
+}
+
+static void padblock(void)
+{
+	while (out_ofs % erase_block_size) {
+		full_write(out_fd, ffbuf, min(sizeof(ffbuf),
+					erase_block_size - (out_ofs % erase_block_size)));
+	}
+}
+
+static void pad(int req)
+{
+	while (req) {
+		if (req > sizeof(ffbuf)) {
+			full_write(out_fd, ffbuf, sizeof(ffbuf));
+			req -= sizeof(ffbuf);
+		} else {
+			full_write(out_fd, ffbuf, req);
+			req = 0;
+		}
+	}
+}
+
+static inline void padword(void)
+{
+	if (out_ofs % 4) {
+		full_write(out_fd, ffbuf, 4 - (out_ofs % 4));
+	}
+}
+
+static inline void pad_block_if_less_than(int req)
+{
+	if (add_cleanmarkers) {
+		if ((out_ofs % erase_block_size) == 0) {
+			full_write(out_fd, &cleanmarker, sizeof(cleanmarker));
+			pad(cleanmarker_size - sizeof(cleanmarker));
+			padword();
+		}
+	}
+	if ((out_ofs % erase_block_size) + req > erase_block_size) {
+		padblock();
+	}
+	if (add_cleanmarkers) {
+		if ((out_ofs % erase_block_size) == 0) {
+			full_write(out_fd, &cleanmarker, sizeof(cleanmarker));
+			pad(cleanmarker_size - sizeof(cleanmarker));
+			padword();
+		}
+	}
+}
+
+static void write_dirent(struct filesystem_entry *e)
+{
+	char *name = e->name;
+	struct jffs2_raw_dirent rd;
+	struct stat *statbuf = &(e->sb);
+	static uint32_t version = 0;
+
+	memset(&rd, 0, sizeof(rd));
+
+	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name));
+	rd.hdr_crc = cpu_to_je32(mtd_crc32(0, &rd,
+				sizeof(struct jffs2_unknown_node) - 4));
+	rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1);
+	rd.version = cpu_to_je32(version++);
+	rd.ino = cpu_to_je32(e->ino);
+	rd.mctime = cpu_to_je32(statbuf->st_mtime);
+	rd.nsize = strlen(name);
+	rd.type = IFTODT(statbuf->st_mode);
+	//rd.unused[0] = 0;
+	//rd.unused[1] = 0;
+	rd.node_crc = cpu_to_je32(mtd_crc32(0, &rd, sizeof(rd) - 8));
+	rd.name_crc = cpu_to_je32(mtd_crc32(0, name, strlen(name)));
+
+	pad_block_if_less_than(sizeof(rd) + rd.nsize);
+	full_write(out_fd, &rd, sizeof(rd));
+	full_write(out_fd, name, rd.nsize);
+	padword();
+}
+
+static unsigned int write_regular_file(struct filesystem_entry *e)
+{
+	int fd, len;
+	uint32_t ver;
+	unsigned int offset;
+	unsigned char *buf, *cbuf, *wbuf;
+	struct jffs2_raw_inode ri;
+	struct stat *statbuf;
+	unsigned int totcomp = 0;
+
+	statbuf = &(e->sb);
+	if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) {
+		errmsg("Skipping file \"%s\" too large.", e->path);
+		return -1;
+	}
+	fd = open(e->hostname, O_RDONLY);
+	if (fd == -1) {
+		sys_errmsg_die("%s: open file", e->hostname);
+	}
+
+	e->ino = ++ino;
+	mkfs_debug_msg("writing file '%s'  ino=%lu  parent_ino=%lu",
+			e->name, (unsigned long) e->ino,
+			(unsigned long) e->parent->ino);
+	write_dirent(e);
+
+	buf = xmalloc(page_size);
+	cbuf = NULL;
+
+	ver = 0;
+	offset = 0;
+
+	memset(&ri, 0, sizeof(ri));
+	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+
+	ri.ino = cpu_to_je32(e->ino);
+	ri.mode = cpu_to_jemode(statbuf->st_mode);
+	ri.uid = cpu_to_je16(statbuf->st_uid);
+	ri.gid = cpu_to_je16(statbuf->st_gid);
+	ri.atime = cpu_to_je32(statbuf->st_atime);
+	ri.ctime = cpu_to_je32(statbuf->st_ctime);
+	ri.mtime = cpu_to_je32(statbuf->st_mtime);
+	ri.isize = cpu_to_je32(statbuf->st_size);
+
+	while ((len = read(fd, buf, page_size))) {
+		unsigned char *tbuf = buf;
+
+		if (len < 0) {
+			sys_errmsg_die("read");
+		}
+
+		while (len) {
+			uint32_t dsize, space;
+			uint16_t compression;
+
+			pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN);
+
+			dsize = len;
+			space =
+				erase_block_size - (out_ofs % erase_block_size) -
+				sizeof(ri);
+			if (space > dsize)
+				space = dsize;
+
+			compression = jffs2_compress(tbuf, &cbuf, &dsize, &space);
+
+			ri.compr = compression & 0xff;
+			ri.usercompr = (compression >> 8) & 0xff;
+
+			if (ri.compr) {
+				wbuf = cbuf;
+			} else {
+				wbuf = tbuf;
+				dsize = space;
+			}
+
+			ri.totlen = cpu_to_je32(sizeof(ri) + space);
+			ri.hdr_crc = cpu_to_je32(mtd_crc32(0,
+						&ri, sizeof(struct jffs2_unknown_node) - 4));
+
+			ri.version = cpu_to_je32(++ver);
+			ri.offset = cpu_to_je32(offset);
+			ri.csize = cpu_to_je32(space);
+			ri.dsize = cpu_to_je32(dsize);
+			ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8));
+			ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space));
+
+			full_write(out_fd, &ri, sizeof(ri));
+			totcomp += sizeof(ri);
+			full_write(out_fd, wbuf, space);
+			totcomp += space;
+			padword();
+
+			if (tbuf != cbuf) {
+				free(cbuf);
+				cbuf = NULL;
+			}
+
+			tbuf += dsize;
+			len -= dsize;
+			offset += dsize;
+
+		}
+	}
+	if (!je32_to_cpu(ri.version)) {
+		/* Was empty file */
+		pad_block_if_less_than(sizeof(ri));
+
+		ri.version = cpu_to_je32(++ver);
+		ri.totlen = cpu_to_je32(sizeof(ri));
+		ri.hdr_crc = cpu_to_je32(mtd_crc32(0,
+					&ri, sizeof(struct jffs2_unknown_node) - 4));
+		ri.csize = cpu_to_je32(0);
+		ri.dsize = cpu_to_je32(0);
+		ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8));
+
+		full_write(out_fd, &ri, sizeof(ri));
+		padword();
+	}
+	free(buf);
+	close(fd);
+	return totcomp;
+}
+
+static void write_symlink(struct filesystem_entry *e)
+{
+	int len;
+	struct stat *statbuf;
+	struct jffs2_raw_inode ri;
+
+	statbuf = &(e->sb);
+	e->ino = ++ino;
+	mkfs_debug_msg("writing symlink '%s'  ino=%lu  parent_ino=%lu",
+			e->name, (unsigned long) e->ino,
+			(unsigned long) e->parent->ino);
+	write_dirent(e);
+
+	len = strlen(e->link);
+	if (len > JFFS2_MAX_SYMLINK_LEN) {
+		errmsg("symlink too large. Truncated to %d chars.",
+				JFFS2_MAX_SYMLINK_LEN);
+		len = JFFS2_MAX_SYMLINK_LEN;
+	}
+
+	memset(&ri, 0, sizeof(ri));
+
+	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri.totlen = cpu_to_je32(sizeof(ri) + len);
+	ri.hdr_crc = cpu_to_je32(mtd_crc32(0,
+				&ri, sizeof(struct jffs2_unknown_node) - 4));
+
+	ri.ino = cpu_to_je32(e->ino);
+	ri.mode = cpu_to_jemode(statbuf->st_mode);
+	ri.uid = cpu_to_je16(statbuf->st_uid);
+	ri.gid = cpu_to_je16(statbuf->st_gid);
+	ri.atime = cpu_to_je32(statbuf->st_atime);
+	ri.ctime = cpu_to_je32(statbuf->st_ctime);
+	ri.mtime = cpu_to_je32(statbuf->st_mtime);
+	ri.isize = cpu_to_je32(statbuf->st_size);
+	ri.version = cpu_to_je32(1);
+	ri.csize = cpu_to_je32(len);
+	ri.dsize = cpu_to_je32(len);
+	ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8));
+	ri.data_crc = cpu_to_je32(mtd_crc32(0, e->link, len));
+
+	pad_block_if_less_than(sizeof(ri) + len);
+	full_write(out_fd, &ri, sizeof(ri));
+	full_write(out_fd, e->link, len);
+	padword();
+}
+
+static void write_pipe(struct filesystem_entry *e)
+{
+	struct stat *statbuf;
+	struct jffs2_raw_inode ri;
+
+	statbuf = &(e->sb);
+	e->ino = ++ino;
+	if (S_ISDIR(statbuf->st_mode)) {
+		mkfs_debug_msg("writing dir '%s'  ino=%lu  parent_ino=%lu",
+				e->name, (unsigned long) e->ino,
+				(unsigned long) (e->parent) ? e->parent->ino : 1);
+	}
+	write_dirent(e);
+
+	memset(&ri, 0, sizeof(ri));
+
+	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri.totlen = cpu_to_je32(sizeof(ri));
+	ri.hdr_crc = cpu_to_je32(mtd_crc32(0,
+				&ri, sizeof(struct jffs2_unknown_node) - 4));
+
+	ri.ino = cpu_to_je32(e->ino);
+	ri.mode = cpu_to_jemode(statbuf->st_mode);
+	ri.uid = cpu_to_je16(statbuf->st_uid);
+	ri.gid = cpu_to_je16(statbuf->st_gid);
+	ri.atime = cpu_to_je32(statbuf->st_atime);
+	ri.ctime = cpu_to_je32(statbuf->st_ctime);
+	ri.mtime = cpu_to_je32(statbuf->st_mtime);
+	ri.isize = cpu_to_je32(0);
+	ri.version = cpu_to_je32(1);
+	ri.csize = cpu_to_je32(0);
+	ri.dsize = cpu_to_je32(0);
+	ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8));
+	ri.data_crc = cpu_to_je32(0);
+
+	pad_block_if_less_than(sizeof(ri));
+	full_write(out_fd, &ri, sizeof(ri));
+	padword();
+}
+
+static void write_special_file(struct filesystem_entry *e)
+{
+	jint16_t kdev;
+	struct stat *statbuf;
+	struct jffs2_raw_inode ri;
+
+	statbuf = &(e->sb);
+	e->ino = ++ino;
+	write_dirent(e);
+
+	kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) +
+			minor(statbuf->st_rdev));
+
+	memset(&ri, 0, sizeof(ri));
+
+	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev));
+	ri.hdr_crc = cpu_to_je32(mtd_crc32(0,
+				&ri, sizeof(struct jffs2_unknown_node) - 4));
+
+	ri.ino = cpu_to_je32(e->ino);
+	ri.mode = cpu_to_jemode(statbuf->st_mode);
+	ri.uid = cpu_to_je16(statbuf->st_uid);
+	ri.gid = cpu_to_je16(statbuf->st_gid);
+	ri.atime = cpu_to_je32(statbuf->st_atime);
+	ri.ctime = cpu_to_je32(statbuf->st_ctime);
+	ri.mtime = cpu_to_je32(statbuf->st_mtime);
+	ri.isize = cpu_to_je32(statbuf->st_size);
+	ri.version = cpu_to_je32(1);
+	ri.csize = cpu_to_je32(sizeof(kdev));
+	ri.dsize = cpu_to_je32(sizeof(kdev));
+	ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8));
+	ri.data_crc = cpu_to_je32(mtd_crc32(0, &kdev, sizeof(kdev)));
+
+	pad_block_if_less_than(sizeof(ri) + sizeof(kdev));
+	full_write(out_fd, &ri, sizeof(ri));
+	full_write(out_fd, &kdev, sizeof(kdev));
+	padword();
+}
+
+#ifndef WITHOUT_XATTR
+typedef struct xattr_entry {
+	struct xattr_entry *next;
+	uint32_t xid;
+	int xprefix;
+	char *xname;
+	char *xvalue;
+	int name_len;
+	int value_len;
+} xattr_entry_t;
+
+#define XATTR_BUFFER_SIZE		(64 * 1024)	/* 64KB */
+static uint32_t enable_xattr = 0;
+static uint32_t highest_xid = 0;
+static uint32_t highest_xseqno = 0;
+
+static struct {
+	int xprefix;
+	const char *string;
+	int length;
+} xprefix_tbl[] = {
+	{ JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN },
+	{ JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
+	{ JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN },
+	{ JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN },
+	{ JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN },
+	{ 0, NULL, 0 }
+};
+
+static void formalize_posix_acl(void *xvalue, int *value_len)
+{
+	struct posix_acl_xattr_header *pacl_header;
+	struct posix_acl_xattr_entry *pent, *plim;
+	struct jffs2_acl_header *jacl_header;
+	struct jffs2_acl_entry *jent;
+	struct jffs2_acl_entry_short *jent_s;
+	char buffer[XATTR_BUFFER_SIZE];
+	int offset = 0;
+
+	pacl_header = xvalue;;
+	pent = pacl_header->a_entries;
+	plim = xvalue + *value_len;
+
+	jacl_header = (struct jffs2_acl_header *)buffer;
+	offset += sizeof(struct jffs2_acl_header);
+	jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
+
+	while (pent < plim) {
+		switch(le16_to_cpu(pent->e_tag)) {
+			case ACL_USER_OBJ:
+			case ACL_GROUP_OBJ:
+			case ACL_MASK:
+			case ACL_OTHER:
+				jent_s = (struct jffs2_acl_entry_short *)(buffer + offset);
+				offset += sizeof(struct jffs2_acl_entry_short);
+				jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag));
+				jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm));
+				break;
+			case ACL_USER:
+			case ACL_GROUP:
+				jent = (struct jffs2_acl_entry *)(buffer + offset);
+				offset += sizeof(struct jffs2_acl_entry);
+				jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag));
+				jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm));
+				jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id));
+				break;
+			default:
+				printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag));
+				exit(1);
+		}
+		pent++;
+	}
+	if (offset > *value_len) {
+		printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n",
+				offset, *value_len);
+		exit(EXIT_FAILURE);
+	}
+	memcpy(xvalue, buffer, offset);
+	*value_len = offset;
+}
+
+static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len)
+{
+	xattr_entry_t *xe;
+	struct jffs2_raw_xattr rx;
+	int name_len;
+
+	/* create xattr entry */
+	name_len = strlen(xname);
+	xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len);
+	xe->next = NULL;
+	xe->xid = ++highest_xid;
+	xe->xprefix = xprefix;
+	xe->xname = ((char *)xe) + sizeof(xattr_entry_t);
+	xe->xvalue = xe->xname + name_len + 1;
+	xe->name_len = name_len;
+	xe->value_len = value_len;
+	strcpy(xe->xname, xname);
+	memcpy(xe->xvalue, xvalue, value_len);
+
+	/* write xattr node */
+	memset(&rx, 0, sizeof(rx));
+	rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
+	rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len));
+	rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4));
+
+	rx.xid = cpu_to_je32(xe->xid);
+	rx.version = cpu_to_je32(1);	/* initial version */
+	rx.xprefix = xprefix;
+	rx.name_len = xe->name_len;
+	rx.value_len = cpu_to_je16(xe->value_len);
+	rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len));
+	rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4));
+
+	pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len);
+	full_write(out_fd, &rx, sizeof(rx));
+	full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len);
+	padword();
+
+	return xe;
+}
+
+#define XATTRENTRY_HASHSIZE	57
+static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len)
+{
+	static xattr_entry_t **xentry_hash = NULL;
+	xattr_entry_t *xe;
+	int index, name_len;
+
+	/* create hash table */
+	if (!xentry_hash)
+		xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE);
+
+	if (xprefix == JFFS2_XPREFIX_ACL_ACCESS
+			|| xprefix == JFFS2_XPREFIX_ACL_DEFAULT)
+		formalize_posix_acl(xvalue, &value_len);
+
+	name_len = strlen(xname);
+	index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE;
+	for (xe = xentry_hash[index]; xe; xe = xe->next) {
+		if (xe->xprefix == xprefix
+				&& xe->value_len == value_len
+				&& !strcmp(xe->xname, xname)
+				&& !memcmp(xe->xvalue, xvalue, value_len))
+			break;
+	}
+	if (!xe) {
+		xe = create_xattr_entry(xprefix, xname, xvalue, value_len);
+		xe->next = xentry_hash[index];
+		xentry_hash[index] = xe;
+	}
+	return xe;
+}
+
+static void write_xattr_entry(struct filesystem_entry *e)
+{
+	struct jffs2_raw_xref ref;
+	struct xattr_entry *xe;
+	char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE];
+	char *xname;
+	const char *prefix_str;
+	int i, xprefix, prefix_len;
+	int list_sz, offset, name_len, value_len;
+
+	if (!enable_xattr)
+		return;
+
+	list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE);
+	if (list_sz < 0) {
+		if (verbose)
+			printf("llistxattr('%s') = %d : %s\n",
+					e->hostname, errno, strerror(errno));
+		return;
+	}
+
+	for (offset = 0; offset < list_sz; offset += name_len) {
+		xname = xlist + offset;
+		name_len = strlen(xname) + 1;
+
+		for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) {
+			prefix_str = xprefix_tbl[i].string;
+			prefix_len = xprefix_tbl[i].length;
+			if (prefix_str[prefix_len - 1] == '.') {
+				if (!strncmp(xname, prefix_str, prefix_len - 1))
+					break;
+			} else {
+				if (!strcmp(xname, prefix_str))
+					break;
+			}
+		}
+		if (!xprefix) {
+			if (verbose)
+				printf("%s: xattr '%s' is not supported.\n",
+						e->hostname, xname);
+			continue;
+		}
+		if ((enable_xattr & (1 << xprefix)) == 0)
+			continue;
+
+		value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE);
+		if (value_len < 0) {
+			if (verbose)
+				printf("lgetxattr('%s', '%s') = %d : %s\n",
+						e->hostname, xname, errno, strerror(errno));
+			continue;
+		}
+		xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len);
+		if (!xe) {
+			if (verbose)
+				printf("%s : xattr '%s' was ignored.\n",
+						e->hostname, xname);
+			continue;
+		}
+
+		memset(&ref, 0, sizeof(ref));
+		ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF);
+		ref.totlen = cpu_to_je32(sizeof(ref));
+		ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4));
+		ref.ino = cpu_to_je32(e->ino);
+		ref.xid = cpu_to_je32(xe->xid);
+		ref.xseqno = cpu_to_je32(highest_xseqno += 2);
+		ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4));
+
+		pad_block_if_less_than(sizeof(ref));
+		full_write(out_fd, &ref, sizeof(ref));
+		padword();
+	}
+}
+
+#else /* WITHOUT_XATTR */
+#define write_xattr_entry(x)
+#endif
+
+static void recursive_populate_directory(struct filesystem_entry *dir)
+{
+	struct filesystem_entry *e;
+	unsigned int wrote;
+
+	if (verbose) {
+		printf("%s\n", dir->fullname);
+	}
+	write_xattr_entry(dir);		/* for '/' */
+
+	e = dir->files;
+	while (e) {
+		if (e->sb.st_nlink >= 1 &&
+		    (e->ino = find_hardlink(e))) {
+
+			write_dirent(e);
+			if (verbose) {
+				printf("\tL %04o %9lu             %5d:%-3d %s\n",
+				       e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino,
+				       (int) (e->sb.st_uid), (int) (e->sb.st_gid),
+				       e->name);
+			}
+		} else switch (e->sb.st_mode & S_IFMT) {
+			case S_IFDIR:
+				if (verbose) {
+					printf("\td %04o %9lld             %5d:%-3d %s\n",
+							e->sb.st_mode & ~S_IFMT, (long long)e->sb.st_size,
+							(int) (e->sb.st_uid), (int) (e->sb.st_gid),
+							e->name);
+				}
+				write_pipe(e);
+				write_xattr_entry(e);
+				break;
+			case S_IFSOCK:
+				if (verbose) {
+					printf("\ts %04o %9lld             %5d:%-3d %s\n",
+							e->sb.st_mode & ~S_IFMT, (long long)e->sb.st_size,
+							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+				}
+				write_pipe(e);
+				write_xattr_entry(e);
+				break;
+			case S_IFIFO:
+				if (verbose) {
+					printf("\tp %04o %9lld             %5d:%-3d %s\n",
+							e->sb.st_mode & ~S_IFMT, (long long)e->sb.st_size,
+							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+				}
+				write_pipe(e);
+				write_xattr_entry(e);
+				break;
+			case S_IFCHR:
+				if (verbose) {
+					printf("\tc %04o %4d,%4d             %5d:%-3d %s\n",
+							e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev),
+							minor(e->sb.st_rdev), (int) e->sb.st_uid,
+							(int) e->sb.st_gid, e->name);
+				}
+				write_special_file(e);
+				write_xattr_entry(e);
+				break;
+			case S_IFBLK:
+				if (verbose) {
+					printf("\tb %04o %4d,%4d             %5d:%-3d %s\n",
+							e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev),
+							minor(e->sb.st_rdev), (int) e->sb.st_uid,
+							(int) e->sb.st_gid, e->name);
+				}
+				write_special_file(e);
+				write_xattr_entry(e);
+				break;
+			case S_IFLNK:
+				if (verbose) {
+					printf("\tl %04o %9lld             %5d:%-3d %s -> %s\n",
+							e->sb.st_mode & ~S_IFMT, (long long)e->sb.st_size,
+							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name,
+							e->link);
+				}
+				write_symlink(e);
+				write_xattr_entry(e);
+				break;
+			case S_IFREG:
+				wrote = write_regular_file(e);
+				write_xattr_entry(e);
+				if (verbose) {
+					printf("\tf %04o %9lld (%9u) %5d:%-3d %s\n",
+							e->sb.st_mode & ~S_IFMT, (long long)e->sb.st_size, wrote,
+							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+				}
+				break;
+			default:
+				errmsg("Unknown mode %o for %s", e->sb.st_mode,
+						e->fullname);
+				break;
+		}
+		e = e->next;
+	}
+
+	e = dir->files;
+	while (e) {
+		if (S_ISDIR(e->sb.st_mode)) {
+			if (e->files) {
+				recursive_populate_directory(e);
+			} else if (verbose) {
+				printf("%s\n", e->fullname);
+			}
+		}
+		e = e->next;
+	}
+}
+
+static void create_target_filesystem(struct filesystem_entry *root)
+{
+	cleanmarker.magic    = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+	cleanmarker.totlen   = cpu_to_je32(cleanmarker_size);
+	cleanmarker.hdr_crc  = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4));
+
+	if (ino == 0)
+		ino = 1;
+
+	root->ino = 1;
+	recursive_populate_directory(root);
+
+	if (pad_fs_size == -1) {
+		padblock();
+	} else {
+		if (pad_fs_size && add_cleanmarkers){
+			padblock();
+			while (out_ofs < pad_fs_size) {
+				full_write(out_fd, &cleanmarker, sizeof(cleanmarker));
+				pad(cleanmarker_size - sizeof(cleanmarker));
+				padblock();
+			}
+		} else {
+			while (out_ofs < pad_fs_size) {
+				full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs));
+			}
+
+		}
+	}
+}
+
+static struct option long_options[] = {
+	{"pad", 2, NULL, 'p'},
+	{"root", 1, NULL, 'r'},
+	{"pagesize", 1, NULL, 's'},
+	{"eraseblock", 1, NULL, 'e'},
+	{"ignore", 1, NULL, 'I'},
+	{"output", 1, NULL, 'o'},
+	{"help", 0, NULL, 'h'},
+	{"verbose", 0, NULL, 'v'},
+	{"version", 0, NULL, 'V'},
+	{"big-endian", 0, NULL, 'b'},
+	{"little-endian", 0, NULL, 'l'},
+	{"no-cleanmarkers", 0, NULL, 'n'},
+	{"cleanmarker", 1, NULL, 'c'},
+	{"squash", 0, NULL, 'q'},
+	{"squash-uids", 0, NULL, 'U'},
+	{"squash-perms", 0, NULL, 'P'},
+	{"faketime", 0, NULL, 'f'},
+	{"devtable", 1, NULL, 'D'},
+	{"compression-mode", 1, NULL, 'm'},
+	{"disable-compressor", 1, NULL, 'x'},
+	{"enable-compressor", 1, NULL, 'X'},
+	{"test-compression", 0, NULL, 't'},
+	{"compressor-priority", 1, NULL, 'y'},
+	{"incremental", 1, NULL, 'i'},
+#ifndef WITHOUT_XATTR
+	{"with-xattr", 0, NULL, 1000 },
+	{"with-selinux", 0, NULL, 1001 },
+	{"with-posix-acl", 0, NULL, 1002 },
+#endif
+	{NULL, 0, NULL, 0}
+};
+
+static const char helptext[] =
+"Usage: mkfs.jffs2 [OPTIONS]\n"
+"Make a JFFS2 file system image from an existing directory tree\n\n"
+"Options:\n"
+"  -p, --pad[=SIZE]        Pad output to SIZE bytes with 0xFF. If SIZE is\n"
+"                          not specified, the output is padded to the end of\n"
+"                          the final erase block\n"
+"  -r, -d, --root=DIR      Build file system from directory DIR (default: cwd)\n"
+"  -s, --pagesize=SIZE     Use page size (max data node size) SIZE.\n"
+"                          Set according to target system's memory management\n"
+"                          page size (default: 4KiB)\n"
+"  -e, --eraseblock=SIZE   Use erase block size SIZE (default: 64KiB)\n"
+"  -c, --cleanmarker=SIZE  Size of cleanmarker (default 12)\n"
+"  -m, --compr-mode=MODE   Select compression mode (default: priority)\n"
+"  -x, --disable-compressor=COMPRESSOR_NAME\n"
+"                          Disable a compressor\n"
+"  -X, --enable-compressor=COMPRESSOR_NAME\n"
+"                          Enable a compressor\n"
+"  -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n"
+"                          Set the priority of a compressor\n"
+"  -L, --list-compressors  Show the list of the available compressors\n"
+"  -t, --test-compression  Call decompress and compare with the original (for test)\n"
+"  -n, --no-cleanmarkers   Don't add a cleanmarker to every eraseblock\n"
+"  -I, --ignore=PATH       Ignore sub directory and file tree below PATH when recursing over the file system\n"
+"  -o, --output=FILE       Output to FILE (default: stdout)\n"
+"  -l, --little-endian     Create a little-endian filesystem\n"
+"  -b, --big-endian        Create a big-endian filesystem\n"
+"  -D, --devtable=FILE     Use the named FILE as a device table file\n"
+"  -f, --faketime          Change all file times to '0' for regression testing\n"
+"  -q, --squash            Squash permissions and owners making all files be owned by root\n"
+"  -U, --squash-uids       Squash owners making all files be owned by root\n"
+"  -P, --squash-perms      Squash permissions on all files\n"
+#ifndef WITHOUT_XATTR
+"      --with-xattr        stuff all xattr entries into image\n"
+"      --with-selinux      stuff only SELinux Labels into jffs2 image\n"
+"      --with-posix-acl    stuff only POSIX ACL entries into jffs2 image\n"
+#endif
+"  -h, --help              Display this help text\n"
+"  -v, --verbose           Verbose operation\n"
+"  -V, --version           Display version information\n"
+"  -i, --incremental=FILE  Parse FILE and generate appendage output for it\n\n";
+
+static int load_next_block(void) {
+
+	int ret;
+	ret = read(in_fd, file_buffer, erase_block_size);
+
+	if(verbose)
+		printf("Load next block : %d bytes read\n",ret);
+
+	return ret;
+}
+
+static void process_buffer(int inp_size) {
+	uint8_t		*p = file_buffer;
+	union jffs2_node_union 	*node;
+	uint16_t	type;
+	int		bitchbitmask = 0;
+	int		obsolete;
+
+	char	name[256];
+
+	while ( p < (file_buffer + inp_size)) {
+
+		node = (union jffs2_node_union *) p;
+
+		/* Skip empty space */
+		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
+			p += 4;
+			continue;
+		}
+
+		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK)	{
+			if (!bitchbitmask++)
+				printf ("Wrong bitmask  at  0x%08zx, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic));
+			p += 4;
+			continue;
+		}
+
+		bitchbitmask = 0;
+
+		type = je16_to_cpu(node->u.nodetype);
+		if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
+			obsolete = 1;
+			type |= JFFS2_NODE_ACCURATE;
+		} else
+			obsolete = 0;
+
+		node->u.nodetype = cpu_to_je16(type);
+
+		switch(je16_to_cpu(node->u.nodetype)) {
+
+			case JFFS2_NODETYPE_INODE:
+				if(verbose)
+					printf ("%8s Inode      node at 0x%08zx, totlen 0x%08x, #ino  %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
+							obsolete ? "Obsolete" : "",
+							p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
+							je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
+							je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));
+
+				if ( je32_to_cpu (node->i.ino) > ino )
+					ino = je32_to_cpu (node->i.ino);
+
+				p += PAD(je32_to_cpu (node->i.totlen));
+				break;
+
+			case JFFS2_NODETYPE_DIRENT:
+				memcpy (name, node->d.name, node->d.nsize);
+				name [node->d.nsize] = 0x0;
+
+				if(verbose)
+					printf ("%8s Dirent     node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino  %8d, nsize %8d, name %s\n",
+							obsolete ? "Obsolete" : "",
+							p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
+							je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
+							node->d.nsize, name);
+
+				p += PAD(je32_to_cpu (node->d.totlen));
+				break;
+
+			case JFFS2_NODETYPE_CLEANMARKER:
+				if (verbose) {
+					printf ("%8s Cleanmarker     at 0x%08zx, totlen 0x%08x\n",
+							obsolete ? "Obsolete" : "",
+							p - file_buffer, je32_to_cpu (node->u.totlen));
+				}
+
+				p += PAD(je32_to_cpu (node->u.totlen));
+				break;
+
+			case JFFS2_NODETYPE_PADDING:
+				if (verbose) {
+					printf ("%8s Padding    node at 0x%08zx, totlen 0x%08x\n",
+							obsolete ? "Obsolete" : "",
+							p - file_buffer, je32_to_cpu (node->u.totlen));
+				}
+
+				p += PAD(je32_to_cpu (node->u.totlen));
+				break;
+
+			case 0xffff:
+				p += 4;
+				break;
+
+			default:
+				if (verbose) {
+					printf ("%8s Unknown    node at 0x%08zx, totlen 0x%08x\n",
+							obsolete ? "Obsolete" : "",
+							p - file_buffer, je32_to_cpu (node->u.totlen));
+				}
+
+				p += PAD(je32_to_cpu (node->u.totlen));
+		}
+	}
+}
+
+static void parse_image(void){
+	int ret;
+
+	file_buffer = xmalloc(erase_block_size);
+
+	while ((ret = load_next_block())) {
+		process_buffer(ret);
+	}
+
+	if (file_buffer)
+		free(file_buffer);
+
+	close(in_fd);
+}
+
+int main(int argc, char **argv)
+{
+	int c, opt;
+	char *cwd;
+	struct stat sb;
+	FILE *devtable = NULL;
+	struct filesystem_entry *root;
+	char *compr_name = NULL;
+	int compr_prior  = -1;
+	int warn_page_size = 0;
+	struct ignorepath_entry* element = ignorepath;
+
+	page_size = sysconf(_SC_PAGESIZE);
+	if (page_size < 0) /* System doesn't know so ... */
+		page_size = 4096; /* ... we make an educated guess */
+	if (page_size != 4096)
+		warn_page_size = 1; /* warn user if page size not 4096 */
+
+	jffs2_compressors_init();
+
+	while ((opt = getopt_long(argc, argv,
+					"D:d:r:s:I:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0)
+	{
+		switch (opt) {
+			case 'D':
+				devtable = xfopen(optarg, "r");
+				if (fstat(fileno(devtable), &sb) < 0)
+					sys_errmsg_die("%s", optarg);
+				if (sb.st_size < 10)
+					errmsg_die("%s: not a proper device table file", optarg);
+				break;
+
+			case 'r':
+			case 'd':	/* for compatibility with mkfs.jffs, genext2fs, etc... */
+				if (rootdir != default_rootdir) {
+					errmsg_die("root directory specified more than once");
+				}
+				rootdir = xstrdup(optarg);
+				break;
+
+			case 's':
+				page_size = strtol(optarg, NULL, 0);
+				warn_page_size = 0; /* set by user, so don't need to warn */
+				break;
+
+			case 'I':
+				printf( "Note: Adding '%s' to ignore Path\n", optarg );
+				element = ignorepath;
+				if ( !ignorepath ) {
+					ignorepath = xmalloc( sizeof( struct ignorepath_entry ) );
+					ignorepath->next = 0;
+					strcpy( &ignorepath->name[0], optarg );
+				} else {
+					while ( element->next ) element = element->next;
+					element->next = xmalloc( sizeof( struct ignorepath_entry ) );
+ 					element->next->next = 0;
+					strcpy( &element->next->name[0], optarg );
+				}
+				printf( "--------- Dumping ignore path list ----------------\n" );
+				element = ignorepath;
+				while ( element ) {
+					printf( "  * '%s'\n", &element->name[0] );
+					element = element->next;
+				}
+				printf( "---------------------------------------------------\n" );
+				break;
+
+			case 'o':
+				if (out_fd != -1) {
+					errmsg_die("output filename specified more than once");
+				}
+				out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644);
+				if (out_fd == -1) {
+					sys_errmsg_die("open output file");
+				}
+				break;
+
+			case 'q':
+				squash_uids = 1;
+				squash_perms = 1;
+				break;
+
+			case 'U':
+				squash_uids = 1;
+				break;
+
+			case 'P':
+				squash_perms = 1;
+				break;
+
+			case 'f':
+				fake_times = 1;
+				break;
+
+			case 'h':
+				puts(helptext);
+				exit(EXIT_SUCCESS);
+			case '?':
+				puts(helptext);
+				exit(EXIT_FAILURE);
+
+			case 'v':
+				verbose = 1;
+				break;
+
+			case 'V':
+				common_print_version();
+				exit(EXIT_SUCCESS);
+
+			case 'e': {
+						  char *next;
+						  unsigned units = 0;
+						  erase_block_size = strtol(optarg, &next, 0);
+						  if (!erase_block_size)
+							  errmsg_die("Unrecognisable erase size\n");
+
+						  if (*next) {
+							  if (!strcmp(next, "KiB")) {
+								  units = 1024;
+							  } else if (!strcmp(next, "MiB")) {
+								  units = 1024 * 1024;
+							  } else {
+								  errmsg_die("Unknown units in erasesize\n");
+							  }
+						  } else {
+							  if (erase_block_size < 0x1000)
+								  units = 1024;
+							  else
+								  units = 1;
+						  }
+						  erase_block_size *= units;
+
+						  /* If it's less than 8KiB, they're not allowed */
+						  if (erase_block_size < 0x2000) {
+							  fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n",
+									  erase_block_size);
+							  erase_block_size = 0x2000;
+						  }
+						  break;
+					  }
+
+			case 'l':
+					  target_endian = __LITTLE_ENDIAN;
+					  break;
+
+			case 'b':
+					  target_endian = __BIG_ENDIAN;
+					  break;
+
+			case 'p':
+					  if (optarg)
+						  pad_fs_size = strtol(optarg, NULL, 0);
+					  else
+						  pad_fs_size = -1;
+					  break;
+			case 'n':
+					  add_cleanmarkers = 0;
+					  break;
+			case 'c':
+					  cleanmarker_size = strtol(optarg, NULL, 0);
+					  if (cleanmarker_size < sizeof(cleanmarker)) {
+						  errmsg_die("cleanmarker size must be >= 12");
+					  }
+					  if (cleanmarker_size >= erase_block_size) {
+						  errmsg_die("cleanmarker size must be < eraseblock size");
+					  }
+					  break;
+			case 'm':
+					  if (jffs2_set_compression_mode_name(optarg)) {
+						  errmsg_die("Unknown compression mode %s", optarg);
+					  }
+					  break;
+			case 'x':
+					  if (jffs2_disable_compressor_name(optarg)) {
+						  errmsg_die("Unknown compressor name %s",optarg);
+					  }
+					  break;
+			case 'X':
+					  if (jffs2_enable_compressor_name(optarg)) {
+						  errmsg_die("Unknown compressor name %s",optarg);
+					  }
+					  break;
+			case 'L':
+					  errmsg_die("\n%s",jffs2_list_compressors());
+					  break;
+			case 't':
+					  jffs2_compression_check_set(1);
+					  break;
+			case 'y':
+					  compr_name = xmalloc(strlen(optarg));
+					  sscanf(optarg,"%d:%s",&compr_prior,compr_name);
+					  if ((compr_prior>=0)&&(compr_name)) {
+						  if (jffs2_set_compressor_priority(compr_name, compr_prior))
+							  exit(EXIT_FAILURE);
+					  }
+					  else {
+						  errmsg_die("Cannot parse %s",optarg);
+					  }
+					  free(compr_name);
+					  break;
+			case 'i':
+					  if (in_fd != -1) {
+						  errmsg_die("(incremental) filename specified more than once");
+					  }
+					  in_fd = open(optarg, O_RDONLY);
+					  if (in_fd == -1) {
+						  sys_errmsg_die("cannot open (incremental) file");
+					  }
+					  break;
+#ifndef WITHOUT_XATTR
+			case 1000:	/* --with-xattr  */
+					  enable_xattr |= (1 << JFFS2_XPREFIX_USER)
+						  | (1 << JFFS2_XPREFIX_SECURITY)
+						  | (1 << JFFS2_XPREFIX_ACL_ACCESS)
+						  | (1 << JFFS2_XPREFIX_ACL_DEFAULT)
+						  | (1 << JFFS2_XPREFIX_TRUSTED);
+					  break;
+			case 1001:	/*  --with-selinux  */
+					  enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY);
+					  break;
+			case 1002:	/*  --with-posix-acl  */
+					  enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS)
+						  | (1 << JFFS2_XPREFIX_ACL_DEFAULT);
+					  break;
+#endif
+		}
+	}
+	if (warn_page_size) {
+		errmsg("Page size for this system is by default %d", page_size);
+		errmsg("Use the --pagesize=SIZE option if this is not what you want");
+	}
+	if (out_fd == -1) {
+		if (isatty(1)) {
+			errmsg_die("%s", helptext);
+		}
+		out_fd = 1;
+	}
+
+	if (chdir(rootdir))
+		sys_errmsg_die("%s", rootdir);
+
+	if (!(cwd = getcwd(0, GETCWD_SIZE)))
+		sys_errmsg_die("getcwd failed");
+
+	if(in_fd != -1)
+		parse_image();
+
+	root = recursive_add_host_directory(NULL, "/", cwd);
+
+	if (devtable)
+		parse_device_table(root, devtable);
+
+	create_target_filesystem(root);
+
+	cleanup(root);
+
+	if (rootdir != default_rootdir)
+		free(rootdir);
+
+	close(out_fd);
+
+	if (verbose) {
+		char *s = jffs2_stats();
+		fprintf(stderr,"\n\n%s",s);
+		free(s);
+	}
+	if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) {
+		fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get());
+	}
+
+	jffs2_compressors_exit();
+
+	return 0;
+}
Index: jffsX-utils
===================================================================
--- jffsX-utils	(nonexistent)
+++ jffsX-utils	(revision 5)

Property changes on: jffsX-utils
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- .	(nonexistent)
+++ .	(revision 5)

Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~