tools/build-R2_19_3/mkfs.jffs2/mkfs.jffs2.c

Go to the documentation of this file.
00001 /* vi: set sw=4 ts=4: */
00002 /*
00003  * Build a JFFS2 image in a file, from a given directory tree.
00004  *
00005  * Copyright 2001, 2002 Red Hat, Inc.
00006  *           2001 David A. Schleef <ds@lineo.com>
00007  *           2002 Axis Communications AB
00008  *           2001, 2002 Erik Andersen <andersen@codepoet.org>
00009  *           2004 University of Szeged, Hungary
00010  *           2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2 of the License, or
00015  * (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025  *
00026  * Cross-endian support added by David Schleef <ds@schleef.org>.
00027  *
00028  * Major architectural rewrite by Erik Andersen <andersen@codepoet.org>
00029  * to allow support for making hard links (though hard links support is
00030  * not yet implemented), and for munging file permissions and ownership
00031  * on the fly using --faketime, --squash, --devtable.   And I plugged a
00032  * few memory leaks, adjusted the error handling and fixed some little
00033  * nits here and there.
00034  *
00035  * I also added a sample device table file.  See device_table.txt
00036  *  -Erik, September 2001
00037  *
00038  * Cleanmarkers support added by Axis Communications AB
00039  *
00040  * Rewritten again.  Cleanly separated host and target filsystem
00041  * activities (mainly so I can reuse all the host handling stuff as I
00042  * rewrite other mkfs utils).  Added a verbose option to list types
00043  * and attributes as files are added to the file system.  Major cleanup
00044  * and scrubbing of the code so it can be read, understood, and
00045  * modified by mere mortals.
00046  *
00047  *  -Erik, November 2002
00048  */
00049 
00050 #undef HAVE_ACL /* Don't have this yet at Axis, so botch the code */
00051 
00052 #define _GNU_SOURCE
00053 #include <sys/types.h>
00054 #include <stdio.h>
00055 #include <sys/stat.h>
00056 #include <unistd.h>
00057 #include <sys/mman.h>
00058 #include <fcntl.h>
00059 #include <dirent.h>
00060 #include <stdlib.h>
00061 #include <errno.h>
00062 #include <string.h>
00063 #include <stdarg.h>
00064 #include <stdint.h>
00065 #include <libgen.h>
00066 #include <ctype.h>
00067 #include <time.h>
00068 #include <getopt.h>
00069 #ifdef HAVE_ACL
00070 #include <sys/xattr.h>
00071 #include <sys/acl.h>
00072 #endif
00073 #include <byteswap.h>
00074 #define crc32 __complete_crap
00075 #include <zlib.h>
00076 #undef crc32
00077 #include "crc32.h"
00078 
00079 /* Do not use the wierd XPG version of basename */
00080 #undef basename
00081 
00082 //#define DMALLOC
00083 //#define mkfs_debug_msg    error_msg
00084 #define mkfs_debug_msg(a...)    { }
00085 #define min(x,y) ({ typeof((x)) _x = (x); typeof((y)) _y = (y); (_x>_y)?_y:_x; })
00086 
00087 #define PAD(x) (((x)+3)&~3)
00088 
00089 struct filesystem_entry {
00090         char *name;                                     /* Name of this directory (think basename) */
00091         char *path;                                     /* Path of this directory (think dirname) */
00092         char *fullname;                         /* Full name of this directory (i.e. path+name) */
00093         char *hostname;                         /* Full path to this file on the host filesystem */
00094         struct stat sb;                         /* Stores directory permissions and whatnot */
00095         char *link;                                     /* Target a symlink points to. */
00096         struct filesystem_entry *parent;        /* Parent directory */
00097         struct filesystem_entry *prev;  /* Only relevant to non-directories */
00098         struct filesystem_entry *next;  /* Only relevant to non-directories */
00099         struct filesystem_entry *files; /* Only relevant to directories */
00100 };
00101 
00102 
00103 static int out_fd = -1;
00104 static int in_fd = -1;
00105 static char default_rootdir[] = ".";
00106 static char *rootdir = default_rootdir;
00107 static int verbose = 0;
00108 static int squash_uids = 0;
00109 static int squash_perms = 0;
00110 static int fake_times = 0;
00111 int target_endian = __BYTE_ORDER;
00112 static const char *const app_name = "mkfs.jffs2";
00113 static const char *const memory_exhausted = "memory exhausted";
00114 
00115 static void verror_msg(const char *s, va_list p)
00116 {
00117         fflush(stdout);
00118         fprintf(stderr, "%s: ", app_name);
00119         vfprintf(stderr, s, p);
00120 }
00121 static void error_msg(const char *s, ...)
00122 {
00123         va_list p;
00124 
00125         va_start(p, s);
00126         verror_msg(s, p);
00127         va_end(p);
00128         putc('\n', stderr);
00129 }
00130 
00131 static void error_msg_and_die(const char *s, ...)
00132 {
00133         va_list p;
00134 
00135         va_start(p, s);
00136         verror_msg(s, p);
00137         va_end(p);
00138         putc('\n', stderr);
00139         exit(EXIT_FAILURE);
00140 }
00141 
00142 static void vperror_msg(const char *s, va_list p)
00143 {
00144         int err = errno;
00145 
00146         if (s == 0)
00147                 s = "";
00148         verror_msg(s, p);
00149         if (*s)
00150                 s = ": ";
00151         fprintf(stderr, "%s%s\n", s, strerror(err));
00152 }
00153 
00154 static void perror_msg(const char *s, ...)
00155 {
00156         va_list p;
00157 
00158         va_start(p, s);
00159         vperror_msg(s, p);
00160         va_end(p);
00161 }
00162 
00163 static void perror_msg_and_die(const char *s, ...)
00164 {
00165         va_list p;
00166 
00167         va_start(p, s);
00168         vperror_msg(s, p);
00169         va_end(p);
00170         exit(EXIT_FAILURE);
00171 }
00172 
00173 #ifndef DMALLOC
00174 extern void *xmalloc(size_t size)
00175 {
00176         void *ptr = malloc(size);
00177 
00178         if (ptr == NULL && size != 0)
00179                 error_msg_and_die(memory_exhausted);
00180         return ptr;
00181 }
00182 
00183 extern void *xcalloc(size_t nmemb, size_t size)
00184 {
00185         void *ptr = calloc(nmemb, size);
00186 
00187         if (ptr == NULL && nmemb != 0 && size != 0)
00188                 error_msg_and_die(memory_exhausted);
00189         return ptr;
00190 }
00191 
00192 extern void *xrealloc(void *ptr, size_t size)
00193 {
00194         ptr = realloc(ptr, size);
00195         if (ptr == NULL && size != 0)
00196                 error_msg_and_die(memory_exhausted);
00197         return ptr;
00198 }
00199 
00200 extern char *xstrdup(const char *s)
00201 {
00202         char *t;
00203 
00204         if (s == NULL)
00205                 return NULL;
00206         t = strdup(s);
00207         if (t == NULL)
00208                 error_msg_and_die(memory_exhausted);
00209         return t;
00210 }
00211 #endif
00212 
00213 extern char *xreadlink(const char *path)
00214 {
00215         static const int GROWBY = 80; /* how large we will grow strings by */
00216 
00217         char *buf = NULL;
00218         int bufsize = 0, readsize = 0;
00219 
00220         do {
00221                 buf = xrealloc(buf, bufsize += GROWBY);
00222                 readsize = readlink(path, buf, bufsize); /* 1st try */
00223                 if (readsize == -1) {
00224                     perror_msg("%s:%s", app_name, path);
00225                     return NULL;
00226                 }
00227         }
00228         while (bufsize < readsize + 1);
00229 
00230         buf[readsize] = '\0';
00231 
00232         return buf;
00233 }
00234 static FILE *xfopen(const char *path, const char *mode)
00235 {
00236         FILE *fp;
00237         if ((fp = fopen(path, mode)) == NULL)
00238                 perror_msg_and_die("%s", path);
00239         return fp;
00240 }
00241 
00242 static struct filesystem_entry *find_filesystem_entry(
00243                 struct filesystem_entry *dir, char *fullname, uint32_t type)
00244 {
00245         struct filesystem_entry *e = dir;
00246 
00247         if (S_ISDIR(dir->sb.st_mode)) {
00248                 e = dir->files;
00249         }
00250         while (e) {
00251                 /* Only bother to do the expensive strcmp on matching file types */
00252                 if (type == (e->sb.st_mode & S_IFMT)) {
00253                         if (S_ISDIR(e->sb.st_mode)) {
00254                                 int len = strlen(e->fullname);
00255 
00256                                 /* Check if we are a parent of the correct path */
00257                                 if (strncmp(e->fullname, fullname, len) == 0) {
00258                                         /* Is this an _exact_ match? */
00259                                         if (strcmp(fullname, e->fullname) == 0) {
00260                                                 return (e);
00261                                         }
00262                                         /* Looks like we found a parent of the correct path */
00263                                         if (fullname[len] == '/') {
00264                                                 if (e->files) {
00265                                                         return (find_filesystem_entry (e, fullname, type));
00266                                                 } else {
00267                                                         return NULL;
00268                                                 }
00269                                         }
00270                                 }
00271                         } else {
00272                                 if (strcmp(fullname, e->fullname) == 0) {
00273                                         return (e);
00274                                 }
00275                         }
00276                 }
00277                 e = e->next;
00278         }
00279         return (NULL);
00280 }
00281 
00282 static struct filesystem_entry *add_host_filesystem_entry(
00283                 char *name, char *path, unsigned long uid, unsigned long gid,
00284                 unsigned long mode, dev_t rdev, struct filesystem_entry *parent)
00285 {
00286         int status;
00287         char *tmp;
00288         struct stat sb;
00289         time_t timestamp = time(NULL);
00290         struct filesystem_entry *entry;
00291 
00292         memset(&sb, 0, sizeof(struct stat));
00293         status = lstat(path, &sb);
00294 
00295         if (status >= 0) {
00296                 /* It is ok for some types of files to not exit on disk (such as
00297                  * device nodes), but if they _do_ exist the specified mode had
00298                  * better match the actual file or strange things will happen.... */
00299                 if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) {
00300                         error_msg_and_die ("%s: file type does not match specified type!", path);
00301                 }
00302                 timestamp = sb.st_mtime;
00303         } else {
00304                 /* If this is a regular file, it _must_ exist on disk */
00305                 if ((mode & S_IFMT) == S_IFREG) {
00306                         error_msg_and_die("%s: does not exist!", path);
00307                 }
00308         }
00309 
00310         /* Squash all permissions so files are owned by root, all
00311          * timestamps are _right now_, and file permissions
00312          * have group and other write removed */
00313         if (squash_uids) {
00314                 uid = gid = 0;
00315         }
00316         if (squash_perms) {
00317                 if (!S_ISLNK(mode)) {
00318                         mode &= ~(S_IWGRP | S_IWOTH);
00319                         mode &= ~(S_ISUID | S_ISGID);
00320                 }
00321         }
00322         if (fake_times) {
00323                 timestamp = 0;
00324         }
00325 
00326         entry = xcalloc(1, sizeof(struct filesystem_entry));
00327 
00328         entry->hostname = xstrdup(path);
00329         entry->fullname = xstrdup(name);
00330         tmp = xstrdup(name);
00331         entry->name = xstrdup(basename(tmp));
00332         free(tmp);
00333         tmp = xstrdup(name);
00334         entry->path = xstrdup(dirname(tmp));
00335         free(tmp);
00336 
00337         entry->sb.st_uid = uid;
00338         entry->sb.st_gid = gid;
00339         entry->sb.st_mode = mode;
00340         entry->sb.st_rdev = rdev;
00341         entry->sb.st_atime = entry->sb.st_ctime =
00342                 entry->sb.st_mtime = timestamp;
00343         if (S_ISREG(mode)) {
00344                 entry->sb.st_size = sb.st_size;
00345         }
00346         if (S_ISLNK(mode)) {
00347                 entry->link = xreadlink(path);
00348                 entry->sb.st_size = strlen(entry->link);
00349         }
00350 
00351         /* This happens only for root */
00352         if (!parent)
00353                 return (entry);
00354 
00355         /* Hook the file into the parent directory */
00356         entry->parent = parent;
00357         if (!parent->files) {
00358                 parent->files = entry;
00359         } else {
00360                 struct filesystem_entry *prev;
00361                 for (prev = parent->files; prev->next; prev = prev->next);
00362                 prev->next = entry;
00363                 entry->prev = prev;
00364         }
00365 
00366         return (entry);
00367 }
00368 
00369 static struct filesystem_entry *recursive_add_host_directory(
00370                 struct filesystem_entry *parent, char *targetpath, char *hostpath)
00371 {
00372         int i, n;
00373         struct stat sb;
00374         char *hpath, *tpath;
00375         struct dirent *dp, **namelist;
00376         struct filesystem_entry *entry;
00377 
00378 
00379         if (lstat(hostpath, &sb)) {
00380                 perror_msg_and_die("%s", hostpath);
00381         }
00382 
00383         entry = add_host_filesystem_entry(targetpath, hostpath,
00384                         sb.st_uid, sb.st_gid, sb.st_mode, 0, parent);
00385 
00386         n = scandir(hostpath, &namelist, 0, alphasort);
00387         if (n < 0) {
00388                 perror_msg_and_die("opening directory %s", hostpath);
00389         }
00390 
00391         for (i=0; i<n; i++)
00392         {
00393                 dp = namelist[i];
00394                 if (dp->d_name[0] == '.' && (dp->d_name[1] == 0 ||
00395                    (dp->d_name[1] == '.' &&  dp->d_name[2] == 0)))
00396                 {
00397                         free(dp);
00398                         continue;
00399                 }
00400 
00401                 asprintf(&hpath, "%s/%s", hostpath, dp->d_name);
00402                 if (lstat(hpath, &sb)) {
00403                         perror_msg_and_die("%s", hpath);
00404                 }
00405                 if (strcmp(targetpath, "/") == 0) {
00406                         asprintf(&tpath, "%s%s", targetpath, dp->d_name);
00407                 } else {
00408                         asprintf(&tpath, "%s/%s", targetpath, dp->d_name);
00409                 }
00410 
00411                 switch (sb.st_mode & S_IFMT) {
00412                         case S_IFDIR:
00413                                 recursive_add_host_directory(entry, tpath, hpath);
00414                                 break;
00415 
00416                         case S_IFREG:
00417                         case S_IFSOCK:
00418                         case S_IFIFO:
00419                         case S_IFLNK:
00420                         case S_IFCHR:
00421                         case S_IFBLK:
00422                                 add_host_filesystem_entry(tpath, hpath, sb.st_uid,
00423                                                 sb.st_gid, sb.st_mode, sb.st_rdev, entry);
00424                                 break;
00425 
00426                         default:
00427                                 error_msg("Unknown file type %o for %s", sb.st_mode, hpath);
00428                                 break;
00429                 }
00430                 free(dp);
00431                 free(hpath);
00432                 free(tpath);
00433         }
00434         free(namelist);
00435         return (entry);
00436 }
00437 
00438 /* the GNU C library has a wonderful scanf("%as", string) which will
00439  allocate the string with the right size, good to avoid buffer overruns.
00440  the following macros use it if available or use a hacky workaround...
00441  */
00442 
00443 #ifdef __GNUC__
00444 #define SCANF_PREFIX "a"
00445 #define SCANF_STRING(s) (&s)
00446 #define GETCWD_SIZE 0
00447 #else
00448 #define SCANF_PREFIX "511"
00449 #define SCANF_STRING(s) (s = malloc(512))
00450 #define GETCWD_SIZE -1
00451 inline int snprintf(char *str, size_t n, const char *fmt, ...)
00452 {
00453         int ret;
00454         va_list ap;
00455 
00456         va_start(ap, fmt);
00457         ret = vsprintf(str, fmt, ap);
00458         va_end(ap);
00459         return ret;
00460 }
00461 #endif
00462 
00463 /*  device table entries take the form of:
00464     <path>      <type> <mode>   <uid>   <gid>   <major> <minor> <start> <inc>   <count>
00465     /dev/mem     c    640       0       0         1       1       0     0         -
00466 
00467     type can be one of:
00468         f       A regular file
00469         d       Directory
00470         c       Character special device file
00471         b       Block special device file
00472         p       Fifo (named pipe)
00473 
00474     I don't bother with symlinks (permissions are irrelevant), hard
00475     links (special cases of regular files), or sockets (why bother).
00476 
00477     Regular files must exist in the target root directory.  If a char,
00478     block, fifo, or directory does not exist, it will be created.
00479 */
00480 static int interpret_table_entry(struct filesystem_entry *root, char *line)
00481 {
00482         char *hostpath;
00483         char type, *name = NULL, *tmp, *dir;
00484         unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
00485         unsigned long start = 0, increment = 1, count = 0;
00486         struct filesystem_entry *parent, *entry;
00487 
00488         if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
00489                  SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor,
00490                  &start, &increment, &count) < 0)
00491         {
00492                 return 1;
00493         }
00494 
00495         if (!strcmp(name, "/")) {
00496                 error_msg_and_die("Device table entries require absolute paths");
00497         }
00498 
00499         asprintf(&hostpath, "%s%s", rootdir, name);
00500 
00501         /* Check if this file already exists... */
00502         switch (type) {
00503                 case 'd':
00504                         mode |= S_IFDIR;
00505                         break;
00506                 case 'f':
00507                         mode |= S_IFREG;
00508                         break;
00509                 case 'p':
00510                         mode |= S_IFIFO;
00511                         break;
00512                 case 'c':
00513                         mode |= S_IFCHR;
00514                         break;
00515                 case 'b':
00516                         mode |= S_IFBLK;
00517                         break;
00518                 default:
00519                         error_msg_and_die("Unsupported file type");
00520         }
00521         entry = find_filesystem_entry(root, name, mode);
00522         if (entry) {
00523                 /* Ok, we just need to fixup the existing entry
00524                  * and we will be all done... */
00525                 entry->sb.st_uid = uid;
00526                 entry->sb.st_gid = gid;
00527                 entry->sb.st_mode = mode;
00528                 if (major && minor) {
00529                         entry->sb.st_rdev = makedev(major, minor);
00530                 }
00531         } else {
00532                 /* If parent is NULL (happens with device table entries),
00533                  * try and find our parent now) */
00534                 tmp = strdup(name);
00535                 dir = dirname(tmp);
00536                 parent = find_filesystem_entry(root, dir, S_IFDIR);
00537                 free(tmp);
00538                 if (parent == NULL) {
00539                         error_msg ("skipping device_table entry '%s': no parent directory!", name);
00540                         free(name);
00541                         free(hostpath);
00542                         return 1;
00543                 }
00544 
00545                 switch (type) {
00546                         case 'd':
00547                                 add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
00548                                 break;
00549                         case 'f':
00550                                 add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
00551                                 break;
00552                         case 'p':
00553                                 add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
00554                                 break;
00555                         case 'c':
00556                         case 'b':
00557                                 if (count > 0) {
00558                                         dev_t rdev;
00559                                         unsigned long i;
00560                                         char *dname, *hpath;
00561 
00562                                         for (i = start; i < count; i++) {
00563                                                 asprintf(&dname, "%s%lu", name, i);
00564                                                 asprintf(&hpath, "%s/%s%lu", rootdir, name, i);
00565                                                 rdev = makedev(major, minor + (i * increment - start));
00566                                                 add_host_filesystem_entry(dname, hpath, uid, gid,
00567                                                                 mode, rdev, parent);
00568                                                 free(dname);
00569                                                 free(hpath);
00570                                         }
00571                                 } else {
00572                                         dev_t rdev = makedev(major, minor);
00573                                         add_host_filesystem_entry(name, hostpath, uid, gid,
00574                                                         mode, rdev, parent);
00575                                 }
00576                                 break;
00577                         default:
00578                                 error_msg_and_die("Unsupported file type");
00579                 }
00580         }
00581         free(name);
00582         free(hostpath);
00583         return 0;
00584 }
00585 
00586 static int parse_device_table(struct filesystem_entry *root, FILE * file)
00587 {
00588         char *line;
00589         int status = 0;
00590         size_t length = 0;
00591 
00592         /* Turn off squash, since we must ensure that values
00593          * entered via the device table are not squashed */
00594         squash_uids = 0;
00595         squash_perms = 0;
00596 
00597         /* Looks ok so far.  The general plan now is to read in one
00598          * line at a time, check for leading comment delimiters ('#'),
00599          * then try and parse the line as a device table.  If we fail
00600          * to parse things, try and help the poor fool to fix their
00601          * device table with a useful error msg... */
00602         line = NULL;
00603         while (getline(&line, &length, file) != -1) {
00604                 /* First trim off any whitespace */
00605                 int len = strlen(line);
00606 
00607                 /* trim trailing whitespace */
00608                 while (len > 0 && isspace(line[len - 1]))
00609                         line[--len] = '\0';
00610                 /* trim leading whitespace */
00611                 memmove(line, &line[strspn(line, " \n\r\t\v")], len);
00612 
00613                 /* How long are we after trimming? */
00614                 len = strlen(line);
00615 
00616                 /* If this is NOT a comment line, try to interpret it */
00617                 if (len && *line != '#') {
00618                         if (interpret_table_entry(root, line))
00619                                 status = 1;
00620                 }
00621 
00622                 free(line);
00623                 line = NULL;
00624         }
00625         fclose(file);
00626 
00627         return status;
00628 }
00629 
00630 static void cleanup(struct filesystem_entry *dir)
00631 {
00632         struct filesystem_entry *e, *prev;
00633 
00634         e = dir->files;
00635         while (e) {
00636                 if (e->name)
00637                         free(e->name);
00638                 if (e->path)
00639                         free(e->path);
00640                 if (e->fullname)
00641                         free(e->fullname);
00642                 e->next = NULL;
00643                 e->name = NULL;
00644                 e->path = NULL;
00645                 e->fullname = NULL;
00646                 e->prev = NULL;
00647                 prev = e;
00648                 if (S_ISDIR(e->sb.st_mode)) {
00649                         cleanup(e);
00650                 }
00651                 e = e->next;
00652                 free(prev);
00653         }
00654 }
00655 
00656 /* Here is where we do the actual creation of the file system */
00657 #include "mtd/jffs2-user.h"
00658 
00659 #define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF
00660 #ifndef JFFS2_MAX_SYMLINK_LEN
00661 #define JFFS2_MAX_SYMLINK_LEN 254
00662 #endif
00663 
00664 static uint32_t ino = 0;
00665 static uint8_t *file_buffer = NULL;             /* file buffer contains the actual erase block*/
00666 static int out_ofs = 0;
00667 static int erase_block_size = 65536;
00668 static int pad_fs_size = 0;
00669 static int add_cleanmarkers = 1;
00670 static struct jffs2_unknown_node cleanmarker;
00671 static int cleanmarker_size = sizeof(cleanmarker);
00672 static unsigned char ffbuf[16] =
00673         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00674         0xff, 0xff, 0xff, 0xff, 0xff
00675 };
00676 
00677 /* We default to 4096, per x86.  When building a fs for
00678  * 64-bit arches and whatnot, use the --pagesize=SIZE option */
00679 int page_size = 4096;
00680 
00681 #include "compr.h"
00682 
00683 static void full_write(int fd, const void *buf, int len)
00684 {
00685         int ret;
00686 
00687         while (len > 0) {
00688                 ret = write(fd, buf, len);
00689 
00690                 if (ret < 0)
00691                         perror_msg_and_die("write");
00692 
00693                 if (ret == 0)
00694                         perror_msg_and_die("write returned zero");
00695 
00696                 len -= ret;
00697                 buf += ret;
00698                 out_ofs += ret;
00699         }
00700 }
00701 
00702 static void padblock(void)
00703 {
00704         while (out_ofs % erase_block_size) {
00705                 full_write(out_fd, ffbuf, min(sizeof(ffbuf),
00706                                         erase_block_size - (out_ofs % erase_block_size)));
00707         }
00708 }
00709 
00710 static void pad(int req)
00711 {
00712         while (req) {
00713                 if (req > sizeof(ffbuf)) {
00714                         full_write(out_fd, ffbuf, sizeof(ffbuf));
00715                         req -= sizeof(ffbuf);
00716                 } else {
00717                         full_write(out_fd, ffbuf, req);
00718                         req = 0;
00719                 }
00720         }
00721 }
00722 
00723 static inline void padword(void)
00724 {
00725         if (out_ofs % 4) {
00726                 full_write(out_fd, ffbuf, 4 - (out_ofs % 4));
00727         }
00728 }
00729 
00730 static inline void pad_block_if_less_than(int req)
00731 {
00732         if (add_cleanmarkers) {
00733                 if ((out_ofs % erase_block_size) == 0) {
00734                         full_write(out_fd, &cleanmarker, sizeof(cleanmarker));
00735                         pad(cleanmarker_size - sizeof(cleanmarker));
00736                         padword();
00737                 }
00738         }
00739         if ((out_ofs % erase_block_size) + req > erase_block_size) {
00740                 padblock();
00741         }
00742         if (add_cleanmarkers) {
00743                 if ((out_ofs % erase_block_size) == 0) {
00744                         full_write(out_fd, &cleanmarker, sizeof(cleanmarker));
00745                         pad(cleanmarker_size - sizeof(cleanmarker));
00746                         padword();
00747                 }
00748         }
00749 }
00750 
00751 static void write_dirent(struct filesystem_entry *e)
00752 {
00753         char *name = e->name;
00754         struct jffs2_raw_dirent rd;
00755         struct stat *statbuf = &(e->sb);
00756         static uint32_t version = 0;
00757 
00758         memset(&rd, 0, sizeof(rd));
00759 
00760         rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
00761         rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
00762         rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name));
00763         rd.hdr_crc = cpu_to_je32(crc32(0, &rd,
00764                                 sizeof(struct jffs2_unknown_node) - 4));
00765         rd.pino = cpu_to_je32((e->parent) ? e->parent->sb.st_ino : 1);
00766         rd.version = cpu_to_je32(version++);
00767         rd.ino = cpu_to_je32(statbuf->st_ino);
00768         rd.mctime = cpu_to_je32(statbuf->st_mtime);
00769         rd.nsize = strlen(name);
00770         rd.type = IFTODT(statbuf->st_mode);
00771         //rd.unused[0] = 0;
00772         //rd.unused[1] = 0;
00773         rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd) - 8));
00774         rd.name_crc = cpu_to_je32(crc32(0, name, strlen(name)));
00775 
00776         pad_block_if_less_than(sizeof(rd) + rd.nsize);
00777         full_write(out_fd, &rd, sizeof(rd));
00778         full_write(out_fd, name, rd.nsize);
00779         padword();
00780 }
00781 
00782 static unsigned int write_regular_file(struct filesystem_entry *e)
00783 {
00784         int fd, len;
00785         uint32_t ver;
00786         unsigned int offset;
00787         unsigned char *buf, *cbuf, *wbuf;
00788         struct jffs2_raw_inode ri;
00789         struct stat *statbuf;
00790         unsigned int totcomp = 0;
00791 
00792         statbuf = &(e->sb);
00793         if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) {
00794                 error_msg("Skipping file \"%s\" too large.", e->path);
00795                 return -1;
00796         }
00797         fd = open(e->hostname, O_RDONLY);
00798         if (fd == -1) {
00799                 perror_msg_and_die("%s: open file", e->hostname);
00800         }
00801 
00802         statbuf->st_ino = ++ino;
00803         mkfs_debug_msg("writing file '%s'  ino=%lu  parent_ino=%lu",
00804                         e->name, (unsigned long) statbuf->st_ino,
00805                         (unsigned long) e->parent->sb.st_ino);
00806         write_dirent(e);
00807 
00808         buf = xmalloc(page_size);
00809         cbuf = NULL;
00810 
00811         ver = 0;
00812         offset = 0;
00813 
00814         memset(&ri, 0, sizeof(ri));
00815         ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
00816         ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
00817 
00818         ri.ino = cpu_to_je32(statbuf->st_ino);
00819         ri.mode = cpu_to_jemode(statbuf->st_mode);
00820         ri.uid = cpu_to_je16(statbuf->st_uid);
00821         ri.gid = cpu_to_je16(statbuf->st_gid);
00822         ri.atime = cpu_to_je32(statbuf->st_atime);
00823         ri.ctime = cpu_to_je32(statbuf->st_ctime);
00824         ri.mtime = cpu_to_je32(statbuf->st_mtime);
00825         ri.isize = cpu_to_je32(statbuf->st_size);
00826 
00827         while ((len = read(fd, buf, page_size))) {
00828                 unsigned char *tbuf = buf;
00829 
00830                 if (len < 0) {
00831                         perror_msg_and_die("read");
00832                 }
00833 
00834                 while (len) {
00835                         uint32_t dsize, space;
00836                         uint16_t compression;
00837 
00838                         pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN);
00839 
00840                         dsize = len;
00841                         space =
00842                                 erase_block_size - (out_ofs % erase_block_size) -
00843                                 sizeof(ri);
00844                         if (space > dsize)
00845                                 space = dsize;
00846 
00847                         compression = jffs2_compress(tbuf, &cbuf, &dsize, &space);
00848 
00849                         ri.compr = compression & 0xff;
00850                         ri.usercompr = (compression >> 8) & 0xff;
00851 
00852                         if (ri.compr) {
00853                                 wbuf = cbuf;
00854                         } else {
00855                                 wbuf = tbuf;
00856                                 dsize = space;
00857                         }
00858 
00859                         ri.totlen = cpu_to_je32(sizeof(ri) + space);
00860                         ri.hdr_crc = cpu_to_je32(crc32(0,
00861                                                 &ri, sizeof(struct jffs2_unknown_node) - 4));
00862 
00863                         ri.version = cpu_to_je32(++ver);
00864                         ri.offset = cpu_to_je32(offset);
00865                         ri.csize = cpu_to_je32(space);
00866                         ri.dsize = cpu_to_je32(dsize);
00867                         ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
00868                         ri.data_crc = cpu_to_je32(crc32(0, wbuf, space));
00869 
00870                         full_write(out_fd, &ri, sizeof(ri));
00871                         totcomp += sizeof(ri);
00872                         full_write(out_fd, wbuf, space);
00873                         totcomp += space;
00874                         padword();
00875 
00876                         if (tbuf != cbuf) {
00877                                 free(cbuf);
00878                                 cbuf = NULL;
00879                         }
00880 
00881                         tbuf += dsize;
00882                         len -= dsize;
00883                         offset += dsize;
00884 
00885                 }
00886         }
00887         if (!je32_to_cpu(ri.version)) {
00888                 /* Was empty file */
00889                 pad_block_if_less_than(sizeof(ri));
00890 
00891                 ri.version = cpu_to_je32(++ver);
00892                 ri.totlen = cpu_to_je32(sizeof(ri));
00893                 ri.hdr_crc = cpu_to_je32(crc32(0,
00894                                         &ri, sizeof(struct jffs2_unknown_node) - 4));
00895                 ri.csize = cpu_to_je32(0);
00896                 ri.dsize = cpu_to_je32(0);
00897                 ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
00898 
00899                 full_write(out_fd, &ri, sizeof(ri));
00900                 padword();
00901         }
00902         free(buf);
00903         close(fd);
00904         return totcomp;
00905 }
00906 
00907 static void write_symlink(struct filesystem_entry *e)
00908 {
00909         int len;
00910         struct stat *statbuf;
00911         struct jffs2_raw_inode ri;
00912 
00913         statbuf = &(e->sb);
00914         statbuf->st_ino = ++ino;
00915         mkfs_debug_msg("writing symlink '%s'  ino=%lu  parent_ino=%lu",
00916                         e->name, (unsigned long) statbuf->st_ino,
00917                         (unsigned long) e->parent->sb.st_ino);
00918         write_dirent(e);
00919 
00920         len = strlen(e->link);
00921         if (len > JFFS2_MAX_SYMLINK_LEN) {
00922                 error_msg("symlink too large. Truncated to %d chars.",
00923                                 JFFS2_MAX_SYMLINK_LEN);
00924                 len = JFFS2_MAX_SYMLINK_LEN;
00925         }
00926 
00927         memset(&ri, 0, sizeof(ri));
00928 
00929         ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
00930         ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
00931         ri.totlen = cpu_to_je32(sizeof(ri) + len);
00932         ri.hdr_crc = cpu_to_je32(crc32(0,
00933                                 &ri, sizeof(struct jffs2_unknown_node) - 4));
00934 
00935         ri.ino = cpu_to_je32(statbuf->st_ino);
00936         ri.mode = cpu_to_jemode(statbuf->st_mode);
00937         ri.uid = cpu_to_je16(statbuf->st_uid);
00938         ri.gid = cpu_to_je16(statbuf->st_gid);
00939         ri.atime = cpu_to_je32(statbuf->st_atime);
00940         ri.ctime = cpu_to_je32(statbuf->st_ctime);
00941         ri.mtime = cpu_to_je32(statbuf->st_mtime);
00942         ri.isize = cpu_to_je32(statbuf->st_size);
00943         ri.version = cpu_to_je32(1);
00944         ri.csize = cpu_to_je32(len);
00945         ri.dsize = cpu_to_je32(len);
00946         ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
00947         ri.data_crc = cpu_to_je32(crc32(0, e->link, len));
00948 
00949         pad_block_if_less_than(sizeof(ri) + len);
00950         full_write(out_fd, &ri, sizeof(ri));
00951         full_write(out_fd, e->link, len);
00952         padword();
00953 }
00954 
00955 static void write_pipe(struct filesystem_entry *e)
00956 {
00957         struct stat *statbuf;
00958         struct jffs2_raw_inode ri;
00959 
00960         statbuf = &(e->sb);
00961         statbuf->st_ino = ++ino;
00962         if (S_ISDIR(statbuf->st_mode)) {
00963                 mkfs_debug_msg("writing dir '%s'  ino=%lu  parent_ino=%lu",
00964                                 e->name, (unsigned long) statbuf->st_ino,
00965                                 (unsigned long) (e->parent) ? e->parent->sb.  st_ino : 1);
00966         }
00967         write_dirent(e);
00968 
00969         memset(&ri, 0, sizeof(ri));
00970 
00971         ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
00972         ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
00973         ri.totlen = cpu_to_je32(sizeof(ri));
00974         ri.hdr_crc = cpu_to_je32(crc32(0,
00975                                 &ri, sizeof(struct jffs2_unknown_node) - 4));
00976 
00977         ri.ino = cpu_to_je32(statbuf->st_ino);
00978         ri.mode = cpu_to_jemode(statbuf->st_mode);
00979         ri.uid = cpu_to_je16(statbuf->st_uid);
00980         ri.gid = cpu_to_je16(statbuf->st_gid);
00981         ri.atime = cpu_to_je32(statbuf->st_atime);
00982         ri.ctime = cpu_to_je32(statbuf->st_ctime);
00983         ri.mtime = cpu_to_je32(statbuf->st_mtime);
00984         ri.isize = cpu_to_je32(0);
00985         ri.version = cpu_to_je32(1);
00986         ri.csize = cpu_to_je32(0);
00987         ri.dsize = cpu_to_je32(0);
00988         ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
00989         ri.data_crc = cpu_to_je32(0);
00990 
00991         pad_block_if_less_than(sizeof(ri));
00992         full_write(out_fd, &ri, sizeof(ri));
00993         padword();
00994 }
00995 
00996 static void write_special_file(struct filesystem_entry *e)
00997 {
00998         jint16_t kdev;
00999         struct stat *statbuf;
01000         struct jffs2_raw_inode ri;
01001 
01002         statbuf = &(e->sb);
01003         statbuf->st_ino = ++ino;
01004         write_dirent(e);
01005 
01006         kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) +
01007                         minor(statbuf->st_rdev));
01008 
01009         memset(&ri, 0, sizeof(ri));
01010 
01011         ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
01012         ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
01013         ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev));
01014         ri.hdr_crc = cpu_to_je32(crc32(0,
01015                                 &ri, sizeof(struct jffs2_unknown_node) - 4));
01016 
01017         ri.ino = cpu_to_je32(statbuf->st_ino);
01018         ri.mode = cpu_to_jemode(statbuf->st_mode);
01019         ri.uid = cpu_to_je16(statbuf->st_uid);
01020         ri.gid = cpu_to_je16(statbuf->st_gid);
01021         ri.atime = cpu_to_je32(statbuf->st_atime);
01022         ri.ctime = cpu_to_je32(statbuf->st_ctime);
01023         ri.mtime = cpu_to_je32(statbuf->st_mtime);
01024         ri.isize = cpu_to_je32(statbuf->st_size);
01025         ri.version = cpu_to_je32(1);
01026         ri.csize = cpu_to_je32(sizeof(kdev));
01027         ri.dsize = cpu_to_je32(sizeof(kdev));
01028         ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
01029         ri.data_crc = cpu_to_je32(crc32(0, &kdev, sizeof(kdev)));
01030 
01031         pad_block_if_less_than(sizeof(ri) + sizeof(kdev));
01032         full_write(out_fd, &ri, sizeof(ri));
01033         full_write(out_fd, &kdev, sizeof(kdev));
01034         padword();
01035 }
01036 
01037 #ifdef HAVE_ACL
01038 typedef struct xattr_entry {
01039         struct xattr_entry *next;
01040         uint32_t xid;
01041         int xprefix;
01042         char *xname;
01043         char *xvalue;
01044         int name_len;
01045         int value_len;
01046 } xattr_entry_t;
01047 
01048 #define XATTR_BUFFER_SIZE               (64 * 1024)     /* 64KB */
01049 static uint32_t enable_xattr = 0;
01050 static uint32_t highest_xid = 0;
01051 static uint32_t highest_xseqno = 0;
01052 
01053 static struct {
01054         int xprefix;
01055         char *string;
01056         int length;
01057 } xprefix_tbl[] = {
01058         { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN },
01059         { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
01060         { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN },
01061         { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN },
01062         { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN },
01063         { 0, NULL, 0 }
01064 };
01065 
01066 static void formalize_posix_acl(void *xvalue, int *value_len)
01067 {
01068         struct posix_acl_xattr_header *pacl_header;
01069         struct posix_acl_xattr_entry *pent, *plim;
01070         struct jffs2_acl_header *jacl_header;
01071         struct jffs2_acl_entry *jent;
01072         struct jffs2_acl_entry_short *jent_s;
01073         char buffer[XATTR_BUFFER_SIZE];
01074         int offset = 0;
01075 
01076         pacl_header = xvalue;;
01077         pent = pacl_header->a_entries;
01078         plim = xvalue + *value_len;
01079 
01080         jacl_header = (struct jffs2_acl_header *)buffer;
01081         offset += sizeof(struct jffs2_acl_header);
01082         jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
01083 
01084         while (pent < plim) {
01085                 switch(le16_to_cpu(pent->e_tag)) {
01086                 case ACL_USER_OBJ:
01087                 case ACL_GROUP_OBJ:
01088                 case ACL_MASK:
01089                 case ACL_OTHER:
01090                         jent_s = (struct jffs2_acl_entry_short *)(buffer + offset);
01091                         offset += sizeof(struct jffs2_acl_entry_short);
01092                         jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag));
01093                         jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm));
01094                         break;
01095                 case ACL_USER:
01096                 case ACL_GROUP:
01097                         jent = (struct jffs2_acl_entry *)(buffer + offset);
01098                         offset += sizeof(struct jffs2_acl_entry);
01099                         jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag));
01100                         jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm));
01101                         jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id));
01102                         break;
01103                 default:
01104                         printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag));
01105                         exit(1);
01106                 }
01107                 pent++;
01108         }
01109         if (offset > *value_len) {
01110                 printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n",
01111                        offset, *value_len);
01112                 exit(1);
01113         }
01114         memcpy(xvalue, buffer, offset);
01115         *value_len = offset;
01116 }
01117 
01118 static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len)
01119 {
01120         xattr_entry_t *xe;
01121         struct jffs2_raw_xattr rx;
01122         int name_len;
01123 
01124         /* create xattr entry */
01125         name_len = strlen(xname);
01126         xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len);
01127         xe->next = NULL;
01128         xe->xid = ++highest_xid;
01129         xe->xprefix = xprefix;
01130         xe->xname = ((char *)xe) + sizeof(xattr_entry_t);
01131         xe->xvalue = xe->xname + name_len + 1;
01132         xe->name_len = name_len;
01133         xe->value_len = value_len;
01134         strcpy(xe->xname, xname);
01135         memcpy(xe->xvalue, xvalue, value_len);
01136 
01137         /* write xattr node */
01138         memset(&rx, 0, sizeof(rx));
01139         rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
01140         rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
01141         rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len));
01142         rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4));
01143 
01144         rx.xid = cpu_to_je32(xe->xid);
01145         rx.version = cpu_to_je32(1);    /* initial version */
01146         rx.xprefix = xprefix;
01147         rx.name_len = xe->name_len;
01148         rx.value_len = cpu_to_je16(xe->value_len);
01149         rx.data_crc = cpu_to_je32(crc32(0, xe->xname, xe->name_len + 1 + xe->value_len));
01150         rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 4));
01151 
01152         pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len);
01153         full_write(out_fd, &rx, sizeof(rx));
01154         full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len);
01155         padword();
01156 
01157         return xe;
01158 }
01159 
01160 #define XATTRENTRY_HASHSIZE     57
01161 static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len)
01162 {
01163         static xattr_entry_t **xentry_hash = NULL;
01164         xattr_entry_t *xe;
01165         int index, name_len;
01166 
01167         /* create hash table */
01168         if (!xentry_hash)
01169                 xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE);
01170 
01171         if (xprefix == JFFS2_XPREFIX_ACL_ACCESS
01172             || xprefix == JFFS2_XPREFIX_ACL_DEFAULT)
01173                 formalize_posix_acl(xvalue, &value_len);
01174 
01175         name_len = strlen(xname);
01176         index = (crc32(0, xname, name_len) ^ crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE;
01177         for (xe = xentry_hash[index]; xe; xe = xe->next) {
01178                 if (xe->xprefix == xprefix
01179                     && xe->value_len == value_len
01180                     && !strcmp(xe->xname, xname)
01181                     && !memcmp(xe->xvalue, xvalue, value_len))
01182                         break;
01183         }
01184         if (!xe) {
01185                 xe = create_xattr_entry(xprefix, xname, xvalue, value_len);
01186                 xe->next = xentry_hash[index];
01187                 xentry_hash[index] = xe;
01188         }
01189         return xe;
01190 }
01191 
01192 static void write_xattr_entry(struct filesystem_entry *e)
01193 {
01194         struct jffs2_raw_xref ref;
01195         struct xattr_entry *xe;
01196         char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE];
01197         char *xname, *prefix_str;
01198         int i, xprefix, prefix_len;
01199         int list_sz, offset, name_len, value_len;
01200 
01201         if (!enable_xattr)
01202                 return;
01203 
01204         list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE);
01205         if (list_sz < 0) {
01206                 if (verbose)
01207                         printf("llistxattr('%s') = %d : %s\n",
01208                                e->hostname, errno, strerror(errno));
01209                 return;
01210         }
01211 
01212         for (offset = 0; offset < list_sz; offset += name_len) {
01213                 xname = xlist + offset;
01214                 name_len = strlen(xname) + 1;
01215 
01216                 for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) {
01217                         prefix_str = xprefix_tbl[i].string;
01218                         prefix_len = xprefix_tbl[i].length;
01219                         if (prefix_str[prefix_len - 1] == '.') {
01220                                 if (!strncmp(xname, prefix_str, prefix_len - 1))
01221                                         break;
01222                         } else {
01223                                 if (!strcmp(xname, prefix_str))
01224                                         break;
01225                         }
01226                 }
01227                 if (!xprefix) {
01228                         if (verbose)
01229                                 printf("%s: xattr '%s' is not supported.\n",
01230                                        e->hostname, xname);
01231                         continue;
01232                 }
01233                 if ((enable_xattr & (1 << xprefix)) == 0)
01234                         continue;
01235 
01236                 value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE);
01237                 if (value_len < 0) {
01238                         if (verbose)
01239                                 printf("lgetxattr('%s', '%s') = %d : %s\n",
01240                                        e->hostname, xname, errno, strerror(errno));
01241                         continue;
01242                 }
01243                 xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len);
01244                 if (!xe) {
01245                         if (verbose)
01246                                 printf("%s : xattr '%s' was ignored.\n",
01247                                        e->hostname, xname);
01248                         continue;
01249                 }
01250 
01251                 memset(&ref, 0, sizeof(ref));
01252                 ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
01253                 ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF);
01254                 ref.totlen = cpu_to_je32(sizeof(ref));
01255                 ref.hdr_crc = cpu_to_je32(crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4));
01256                 ref.ino = cpu_to_je32(e->sb.st_ino);
01257                 ref.xid = cpu_to_je32(xe->xid);
01258                 ref.xseqno = cpu_to_je32(highest_xseqno += 2);
01259                 ref.node_crc = cpu_to_je32(crc32(0, &ref, sizeof(ref) - 4));
01260 
01261                 pad_block_if_less_than(sizeof(ref));
01262                 full_write(out_fd, &ref, sizeof(ref));
01263                 padword();
01264         }
01265 }
01266 #else
01267 /* don't do anything, corresponds to  if (!enable_xattr) return;  above */
01268 #define write_xattr_entry(x)
01269 #endif
01270 
01271 static void recursive_populate_directory(struct filesystem_entry *dir)
01272 {
01273         struct filesystem_entry *e;
01274         unsigned int wrote;
01275 
01276         if (verbose) {
01277                 printf("%s\n", dir->fullname);
01278         }
01279         write_xattr_entry(dir);         /* for '/' */
01280 
01281         e = dir->files;
01282         while (e) {
01283 
01284                 switch (e->sb.st_mode & S_IFMT) {
01285                         case S_IFDIR:
01286                                 if (verbose) {
01287                                         printf("\td %04o %9lu             %5d:%-3d %s\n",
01288                                                         e->sb.st_mode & ~S_IFMT, e->sb.st_size,
01289                                                         (int) (e->sb.st_uid), (int) (e->sb.st_gid),
01290                                                         e->name);
01291                                 }
01292                                 write_pipe(e);
01293                                 write_xattr_entry(e);
01294                                 break;
01295                         case S_IFSOCK:
01296                                 if (verbose) {
01297                                         printf("\ts %04o %9lu             %5d:%-3d %s\n",
01298                                                         e->sb.st_mode & ~S_IFMT, e->sb.st_size,
01299                                                         (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
01300                                 }
01301                                 write_pipe(e);
01302                                 write_xattr_entry(e);
01303                                 break;
01304                         case S_IFIFO:
01305                                 if (verbose) {
01306                                         printf("\tp %04o %9lu             %5d:%-3d %s\n",
01307                                                         e->sb.st_mode & ~S_IFMT, e->sb.st_size,
01308                                                         (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
01309                                 }
01310                                 write_pipe(e);
01311                                 write_xattr_entry(e);
01312                                 break;
01313                         case S_IFCHR:
01314                                 if (verbose) {
01315                                         printf("\tc %04o %4d,%4d             %5d:%-3d %s\n",
01316                                                         e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev),
01317                                                         minor(e->sb.st_rdev), (int) e->sb.st_uid,
01318                                                         (int) e->sb.st_gid, e->name);
01319                                 }
01320                                 write_special_file(e);
01321                                 write_xattr_entry(e);
01322                                 break;
01323                         case S_IFBLK:
01324                                 if (verbose) {
01325                                         printf("\tb %04o %4d,%4d             %5d:%-3d %s\n",
01326                                                         e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev),
01327                                                         minor(e->sb.st_rdev), (int) e->sb.st_uid,
01328                                                         (int) e->sb.st_gid, e->name);
01329                                 }
01330                                 write_special_file(e);
01331                                 write_xattr_entry(e);
01332                                 break;
01333                         case S_IFLNK:
01334                                 if (verbose) {
01335                                         printf("\tl %04o %9lu             %5d:%-3d %s -> %s\n",
01336                                                         e->sb.st_mode & ~S_IFMT, e->sb.st_size,
01337                                                         (int) e->sb.st_uid, (int) e->sb.st_gid, e->name,
01338                                                         e->link);
01339                                 }
01340                                 write_symlink(e);
01341                                 write_xattr_entry(e);
01342                                 break;
01343                         case S_IFREG:
01344                                 wrote = write_regular_file(e);
01345                                 write_xattr_entry(e);
01346                                 if (verbose) {
01347                                         printf("\tf %04o %9lu (%9u) %5d:%-3d %s\n",
01348                                                         e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote,
01349                                                         (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
01350                                 }
01351                                 break;
01352                         default:
01353                                 error_msg("Unknown mode %o for %s", e->sb.st_mode,
01354                                                 e->fullname);
01355                                 break;
01356                 }
01357                 e = e->next;
01358         }
01359 
01360         e = dir->files;
01361         while (e) {
01362                 if (S_ISDIR(e->sb.st_mode)) {
01363                         if (e->files) {
01364                                 recursive_populate_directory(e);
01365                         } else if (verbose) {
01366                                 printf("%s\n", e->fullname);
01367                         }
01368                 }
01369                 e = e->next;
01370         }
01371 }
01372 
01373 static void create_target_filesystem(struct filesystem_entry *root)
01374 {
01375         cleanmarker.magic    = cpu_to_je16(JFFS2_MAGIC_BITMASK);
01376         cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
01377         cleanmarker.totlen   = cpu_to_je32(cleanmarker_size);
01378         cleanmarker.hdr_crc  = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4));
01379 
01380         if (ino == 0)
01381                 ino = 1;
01382 
01383         root->sb.st_ino = 1;
01384         recursive_populate_directory(root);
01385 
01386         if (pad_fs_size == -1) {
01387                 padblock();
01388         } else {
01389                 if (pad_fs_size && add_cleanmarkers){
01390                         padblock();
01391                         while (out_ofs < pad_fs_size) {
01392                                 full_write(out_fd, &cleanmarker, sizeof(cleanmarker));
01393                                 pad(cleanmarker_size - sizeof(cleanmarker));
01394                                 padblock();
01395                         }
01396                 } else {
01397                         while (out_ofs < pad_fs_size) {
01398                                 full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs));
01399                         }
01400 
01401                 }
01402         }
01403 }
01404 
01405 static struct option long_options[] = {
01406         {"pad", 2, NULL, 'p'},
01407         {"root", 1, NULL, 'r'},
01408         {"pagesize", 1, NULL, 's'},
01409         {"eraseblock", 1, NULL, 'e'},
01410         {"output", 1, NULL, 'o'},
01411         {"help", 0, NULL, 'h'},
01412         {"verbose", 0, NULL, 'v'},
01413         {"version", 0, NULL, 'V'},
01414         {"big-endian", 0, NULL, 'b'},
01415         {"little-endian", 0, NULL, 'l'},
01416         {"no-cleanmarkers", 0, NULL, 'n'},
01417         {"cleanmarkers", 0, NULL, 0},
01418         {"cleanmarker", 1, NULL, 'c'},
01419         {"squash", 0, NULL, 'q'},
01420         {"squash-uids", 0, NULL, 'U'},
01421         {"squash-perms", 0, NULL, 'P'},
01422         {"faketime", 0, NULL, 'f'},
01423         {"devtable", 1, NULL, 'D'},
01424         {"compression-mode", 1, NULL, 'm'},
01425         {"disable-compressor", 1, NULL, 'x'},
01426         {"test-compression", 0, NULL, 't'},
01427         {"compressor-priority", 1, NULL, 'y'},
01428         {"incremental", 1, NULL, 'i'},
01429         {"with-xattr", 0, NULL, 1000 },
01430         {"with-selinux", 0, NULL, 1001 },
01431         {"with-posix-acl", 0, NULL, 1002 },
01432         {NULL, 0, NULL, 0}
01433 };
01434 
01435 static char *helptext =
01436         "Usage: mkfs.jffs2 [OPTIONS]\n"
01437         "Make a JFFS2 file system image from an existing directory tree\n\n"
01438         "Options:\n"
01439         "  -p, --pad[=SIZE]        Pad output to SIZE bytes with 0xFF. If SIZE is\n"
01440         "                          not specified, the output is padded to the end of\n"
01441         "                          the final erase block\n"
01442         "  -r, -d, --root=DIR      Build file system from directory DIR (default: cwd)\n"
01443         "  -s, --pagesize=SIZE     Use page size (max data node size) SIZE (default: 4KiB)\n"
01444         "  -e, --eraseblock=SIZE   Use erase block size SIZE (default: 64KiB)\n"
01445         "  -c, --cleanmarker=SIZE  Size of cleanmarker (default 12)\n"
01446         "  -m, --compr-mode=MODE   Select compression mode (default: priortiry)\n"
01447         "  -x, --disable-compressor=COMPRESSOR_NAME\n"
01448         "                          Disable a compressor\n"
01449         "  -X, --enable-compressor=COMPRESSOR_NAME\n"
01450         "                          Enable a compressor\n"
01451         "  -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n"
01452         "                          Set the priority of a compressor\n"
01453         "  -L, --list-compressors  Show the list of the avaiable compressors\n"
01454         "  -t, --test-compression  Call decompress and compare with the original (for test)\n"
01455         "  -n, --no-cleanmarkers   Don't add a cleanmarker to every eraseblock\n"
01456         "      --cleanmarkers      Backwards compatibility flag (no-op)\n"
01457         "  -o, --output=FILE       Output to FILE (default: stdout)\n"
01458         "  -l, --little-endian     Create a little-endian filesystem\n"
01459         "  -b, --big-endian        Create a big-endian filesystem\n"
01460         "  -D, --devtable=FILE     Use the named FILE as a device table file\n"
01461         "  -f, --faketime          Change all file times to '0' for regression testing\n"
01462         "  -q, --squash            Squash permissions and owners making all files be owned by root\n"
01463         "  -U, --squash-uids       Squash owners making all files be owned by root\n"
01464         "  -P, --squash-perms      Squash permissions on all files\n"
01465 #ifdef HAVE_ACL
01466         "      --with-xattr        stuff all xattr entries into image\n"
01467         "      --with-selinux      stuff only SELinux Labels into jffs2 image\n"
01468         "      --with-posix-acl    stuff only POSIX ACL entries into jffs2 image\n"
01469 #endif
01470         "  -h, --help              Display this help text\n"
01471         "  -v, --verbose           Verbose operation\n"
01472         "  -V, --version           Display version information\n"
01473         "  -i, --incremental=FILE  Parse FILE and generate appendage output for it\n\n";
01474 
01475 static char *revtext = "$Revision: 1.1.1.1 $";
01476 
01477 int load_next_block() {
01478 
01479         int ret;
01480         ret = read(in_fd, file_buffer, erase_block_size);
01481 
01482         if(verbose)
01483                 printf("Load next block : %d bytes read\n",ret);
01484 
01485         return ret;
01486 }
01487 
01488 void process_buffer(int inp_size) {
01489         uint8_t         *p = file_buffer;
01490         union jffs2_node_union  *node;
01491         uint16_t        type;
01492         int             bitchbitmask = 0;
01493         int             obsolete;
01494 
01495         char    name[256];
01496 
01497         while ( p < (file_buffer + inp_size)) {
01498 
01499                 node = (union jffs2_node_union *) p;
01500 
01501                 /* Skip empty space */
01502                 if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
01503                         p += 4;
01504                         continue;
01505                 }
01506 
01507                 if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
01508                         if (!bitchbitmask++)
01509                             printf ("Wrong bitmask  at  0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic));
01510                         p += 4;
01511                         continue;
01512                 }
01513 
01514                 bitchbitmask = 0;
01515 
01516                 type = je16_to_cpu(node->u.nodetype);
01517                 if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
01518                         obsolete = 1;
01519                         type |= JFFS2_NODE_ACCURATE;
01520                 } else
01521                         obsolete = 0;
01522 
01523                 node->u.nodetype = cpu_to_je16(type);
01524 
01525                 switch(je16_to_cpu(node->u.nodetype)) {
01526 
01527                         case JFFS2_NODETYPE_INODE:
01528                                 if(verbose)
01529                                         printf ("%8s Inode      node at 0x%08x, totlen 0x%08x, #ino  %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
01530                                                 obsolete ? "Obsolete" : "",
01531                                                 p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
01532                                                 je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
01533                                                 je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));
01534 
01535                                 if ( je32_to_cpu (node->i.ino) > ino )
01536                                         ino = je32_to_cpu (node->i.ino);
01537 
01538                                 p += PAD(je32_to_cpu (node->i.totlen));
01539                                 break;
01540 
01541                         case JFFS2_NODETYPE_DIRENT:
01542                                 memcpy (name, node->d.name, node->d.nsize);
01543                                 name [node->d.nsize] = 0x0;
01544 
01545                                 if(verbose)
01546                                         printf ("%8s Dirent     node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino  %8d, nsize %8d, name %s\n",
01547                                                 obsolete ? "Obsolete" : "",
01548                                                 p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
01549                                                 je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
01550                                                 node->d.nsize, name);
01551 
01552                                 p += PAD(je32_to_cpu (node->d.totlen));
01553                                 break;
01554 
01555                         case JFFS2_NODETYPE_CLEANMARKER:
01556                                 if (verbose) {
01557                                         printf ("%8s Cleanmarker     at 0x%08x, totlen 0x%08x\n",
01558                                                 obsolete ? "Obsolete" : "",
01559                                                 p - file_buffer, je32_to_cpu (node->u.totlen));
01560                                 }
01561 
01562                                 p += PAD(je32_to_cpu (node->u.totlen));
01563                                 break;
01564 
01565                         case JFFS2_NODETYPE_PADDING:
01566                                 if (verbose) {
01567                                         printf ("%8s Padding    node at 0x%08x, totlen 0x%08x\n",
01568                                                 obsolete ? "Obsolete" : "",
01569                                                 p - file_buffer, je32_to_cpu (node->u.totlen));
01570                                 }
01571 
01572                                 p += PAD(je32_to_cpu (node->u.totlen));
01573                                 break;
01574 
01575                         case 0xffff:
01576                                 p += 4;
01577                                 break;
01578 
01579                         default:
01580                                 if (verbose) {
01581                                         printf ("%8s Unknown    node at 0x%08x, totlen 0x%08x\n",
01582                                                 obsolete ? "Obsolete" : "",
01583                                                 p - file_buffer, je32_to_cpu (node->u.totlen));
01584                                 }
01585 
01586                                 p += PAD(je32_to_cpu (node->u.totlen));
01587                 }
01588         }
01589 }
01590 
01591 void parse_image(){
01592         int ret;
01593 
01594         file_buffer = malloc(erase_block_size);
01595 
01596         if (!file_buffer) {
01597                 perror("out of memory");
01598                 close (in_fd);
01599                 close (out_fd);
01600                 exit(1);
01601         }
01602 
01603         while ((ret = load_next_block())) {
01604                 process_buffer(ret);
01605         }
01606 
01607         if (file_buffer)
01608                 free(file_buffer);
01609 
01610         close(in_fd);
01611 }
01612 
01613 int main(int argc, char **argv)
01614 {
01615         int c, opt;
01616         char *cwd;
01617         struct stat sb;
01618         FILE *devtable = NULL;
01619         struct filesystem_entry *root;
01620         char *compr_name = NULL;
01621         int compr_prior  = -1;
01622 
01623         jffs2_compressors_init();
01624 
01625         while ((opt = getopt_long(argc, argv,
01626                                         "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0)
01627         {
01628                 switch (opt) {
01629                         case 'D':
01630                                 devtable = xfopen(optarg, "r");
01631                                 if (fstat(fileno(devtable), &sb) < 0)
01632                                         perror_msg_and_die(optarg);
01633                                 if (sb.st_size < 10)
01634                                         error_msg_and_die("%s: not a proper device table file", optarg);
01635                                 break;
01636 
01637                         case 'r':
01638                         case 'd':       /* for compatibility with mkfs.jffs, genext2fs, etc... */
01639                                 if (rootdir != default_rootdir) {
01640                                         error_msg_and_die("root directory specified more than once");
01641                                 }
01642                                 rootdir = xstrdup(optarg);
01643                                 break;
01644 
01645                         case 's':
01646                                 page_size = strtol(optarg, NULL, 0);
01647                                 break;
01648 
01649                         case 'o':
01650                                 if (out_fd != -1) {
01651                                         error_msg_and_die("output filename specified more than once");
01652                                 }
01653                                 out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644);
01654                                 if (out_fd == -1) {
01655                                         perror_msg_and_die("open output file");
01656                                 }
01657                                 break;
01658 
01659                         case 'q':
01660                                 squash_uids = 1;
01661                                 squash_perms = 1;
01662                                 break;
01663 
01664                         case 'U':
01665                                 squash_uids = 1;
01666                                 break;
01667 
01668                         case 'P':
01669                                 squash_perms = 1;
01670                                 break;
01671 
01672                         case 'f':
01673                                 fake_times = 1;
01674                                 break;
01675 
01676                         case 'h':
01677                         case '?':
01678                                 error_msg_and_die(helptext);
01679 
01680                         case 'v':
01681                                 verbose = 1;
01682                                 break;
01683 
01684                         case 'V':
01685                                 error_msg_and_die("revision %.*s\n",
01686                                                 (int) strlen(revtext) - 13, revtext + 11);
01687 
01688                         case 'e': {
01689                                 char *next;
01690                                 unsigned units = 0;
01691                                 erase_block_size = strtol(optarg, &next, 0);
01692                                 if (!erase_block_size)
01693                                         error_msg_and_die("Unrecognisable erase size\n");
01694 
01695                                 if (*next) {
01696                                         if (!strcmp(next, "KiB")) {
01697                                                 units = 1024;
01698                                         } else if (!strcmp(next, "MiB")) {
01699                                                 units = 1024 * 1024;
01700                                         } else {
01701                                                 error_msg_and_die("Unknown units in erasesize\n");
01702                                         }
01703                                 } else {
01704                                         if (erase_block_size < 0x1000)
01705                                                 units = 1024;
01706                                         else
01707                                                 units = 1;
01708                                 }
01709                                 erase_block_size *= units;
01710 
01711                                 /* If it's less than 64KiB, they're not allowed */
01712                                 if (erase_block_size < 0x10000) {
01713                                         fprintf(stderr, "Erase size 0x%x too small. Increasing to 64KiB minimum\n",
01714                                                 erase_block_size);
01715                                         erase_block_size = 0x10000;
01716                                 }
01717                                 break;
01718                         }
01719 
01720                         case 'l':
01721                                 target_endian = __LITTLE_ENDIAN;
01722                                 break;
01723 
01724                         case 'b':
01725                                 target_endian = __BIG_ENDIAN;
01726                                 break;
01727 
01728                         case 'p':
01729                                 if (optarg)
01730                                         pad_fs_size = strtol(optarg, NULL, 0);
01731                                 else
01732                                         pad_fs_size = -1;
01733                                 break;
01734                         case 'n':
01735                                 add_cleanmarkers = 0;
01736                                 break;
01737                         case 'c':
01738                                 cleanmarker_size = strtol(optarg, NULL, 0);
01739                                 if (cleanmarker_size < sizeof(cleanmarker)) {
01740                                         error_msg_and_die("cleanmarker size must be >= 12");
01741                                 }
01742                                 if (cleanmarker_size >= erase_block_size) {
01743                                         error_msg_and_die("cleanmarker size must be < eraseblock size");
01744                                 }
01745                                 break;
01746                         case 'm':
01747                                 if (jffs2_set_compression_mode_name(optarg)) {
01748                                         error_msg_and_die("Unknown compression mode %s", optarg);
01749                                 }
01750                                 break;
01751                         case 'x':
01752                                 if (jffs2_disable_compressor_name(optarg)) {
01753                                         error_msg_and_die("Unknown compressor name %s",optarg);
01754                                 }
01755                                 break;
01756                         case 'X':
01757                                 if (jffs2_enable_compressor_name(optarg)) {
01758                                         error_msg_and_die("Unknown compressor name %s",optarg);
01759                                 }
01760                                 break;
01761                         case 'L':
01762                                 error_msg_and_die("\n%s",jffs2_list_compressors());
01763                                 break;
01764                         case 't':
01765                                 jffs2_compression_check_set(1);
01766                                 break;
01767                         case 'y':
01768                                 compr_name = malloc(strlen(optarg));
01769                                 sscanf(optarg,"%d:%s",&compr_prior,compr_name);
01770                                 if ((compr_prior>=0)&&(compr_name)) {
01771                                         if (jffs2_set_compressor_priority(compr_name, compr_prior))
01772                                                 exit(EXIT_FAILURE);
01773                                 }
01774                                 else {
01775                                         error_msg_and_die("Cannot parse %s",optarg);
01776                                 }
01777                                 free(compr_name);
01778                                 break;
01779                         case 'i':
01780                                 if (in_fd != -1) {
01781                                         error_msg_and_die("(incremental) filename specified more than once");
01782                                 }
01783                                 in_fd = open(optarg, O_RDONLY);
01784                                 if (in_fd == -1) {
01785                                         perror_msg_and_die("cannot open (incremental) file");
01786                                 }
01787                                 break;
01788 #ifdef HAVE_ACL
01789                         case 1000:      /* --with-xattr  */
01790                                 enable_xattr |= (1 << JFFS2_XPREFIX_USER)
01791                                                 | (1 << JFFS2_XPREFIX_SECURITY)
01792                                                 | (1 << JFFS2_XPREFIX_ACL_ACCESS)
01793                                                 | (1 << JFFS2_XPREFIX_ACL_DEFAULT)
01794                                                 | (1 << JFFS2_XPREFIX_TRUSTED);
01795                                 break;
01796                         case 1001:      /*  --with-selinux  */
01797                                 enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY);
01798                                 break;
01799                         case 1002:      /*  --with-posix-acl  */
01800                                 enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS)
01801                                                 | (1 << JFFS2_XPREFIX_ACL_DEFAULT);
01802                                 break;
01803 #endif
01804                 }
01805         }
01806         if (out_fd == -1) {
01807                 if (isatty(1)) {
01808                         error_msg_and_die(helptext);
01809                 }
01810                 out_fd = 1;
01811         }
01812         if (lstat(rootdir, &sb)) {
01813                 perror_msg_and_die("%s", rootdir);
01814         }
01815         if (chdir(rootdir))
01816                 perror_msg_and_die("%s", rootdir);
01817 
01818         if (!(cwd = getcwd(0, GETCWD_SIZE)))
01819                 perror_msg_and_die("getcwd failed");
01820 
01821         if(in_fd != -1)
01822                 parse_image();
01823 
01824         root = recursive_add_host_directory(NULL, "/", cwd);
01825 
01826         if (devtable)
01827                 parse_device_table(root, devtable);
01828 
01829         create_target_filesystem(root);
01830 
01831         cleanup(root);
01832 
01833         if (rootdir != default_rootdir)
01834                 free(rootdir);
01835 
01836         close(out_fd);
01837 
01838         if (verbose) {
01839                 char *s = jffs2_stats();
01840                 fprintf(stderr,"\n\n%s",s);
01841                 free(s);
01842         }
01843         if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) {
01844                 fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get());
01845         }
01846 
01847         jffs2_compressors_exit();
01848 
01849         return 0;
01850 }

Generated on Fri Nov 28 00:06:25 2008 for elphel by  doxygen 1.5.1