12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811 |
- /* 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 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;
- 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);
- 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'},
- {"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"
- " -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;
- 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: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 '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;
- }
|