00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050 #undef HAVE_ACL
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
00080 #undef basename
00081
00082
00083
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;
00091 char *path;
00092 char *fullname;
00093 char *hostname;
00094 struct stat sb;
00095 char *link;
00096 struct filesystem_entry *parent;
00097 struct filesystem_entry *prev;
00098 struct filesystem_entry *next;
00099 struct filesystem_entry *files;
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;
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);
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
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
00257 if (strncmp(e->fullname, fullname, len) == 0) {
00258
00259 if (strcmp(fullname, e->fullname) == 0) {
00260 return (e);
00261 }
00262
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
00297
00298
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
00305 if ((mode & S_IFMT) == S_IFREG) {
00306 error_msg_and_die("%s: does not exist!", path);
00307 }
00308 }
00309
00310
00311
00312
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
00352 if (!parent)
00353 return (entry);
00354
00355
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
00439
00440
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
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
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
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
00524
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
00533
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
00593
00594 squash_uids = 0;
00595 squash_perms = 0;
00596
00597
00598
00599
00600
00601
00602 line = NULL;
00603 while (getline(&line, &length, file) != -1) {
00604
00605 int len = strlen(line);
00606
00607
00608 while (len > 0 && isspace(line[len - 1]))
00609 line[--len] = '\0';
00610
00611 memmove(line, &line[strspn(line, " \n\r\t\v")], len);
00612
00613
00614 len = strlen(line);
00615
00616
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
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;
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
00678
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
00772
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
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)
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
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
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);
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
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
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);
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
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':
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
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:
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:
01797 enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY);
01798 break;
01799 case 1002:
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 }