1 /* vi: set sw=4 ts=4: */
3 * jffs2reader v0.0.18 A jffs2 image reader
5 * Copyright (c) 2001 Jari Kirma <Jari.Kirma@hut.fi>
7 * This software is provided 'as-is', without any express or implied
8 * warranty. In no event will the author be held liable for any damages
9 * arising from the use of this software.
11 * Permission is granted to anyone to use this software for any
12 * purpose, including commercial applications, and to alter it and
13 * redistribute it freely, subject to the following restrictions:
15 * 1. The origin of this software must not be misrepresented; you must
16 * not claim that you wrote the original software. If you use this
17 * software in a product, an acknowledgment in the product
18 * documentation would be appreciated but is not required.
20 * 2. Altered source versions must be plainly marked as such, and must
21 * not be misrepresented as being the original software.
23 * 3. This notice may not be removed or altered from any source
28 * This code was altered September 2001
29 * Changes are Copyright (c) Erik Andersen <andersen@codepoet.org>
31 * In compliance with (2) above, this is hereby marked as an altered
32 * version of this software. It has been altered as follows:
33 * *) Listing a directory now mimics the behavior of 'ls -l'
34 * *) Support for recursive listing has been added
35 * *) Without options, does a recursive 'ls' on the whole filesystem
36 * *) option parsing now uses getopt()
37 * *) Now uses printf, and error messages go to stderr.
38 * *) The copyright notice has been cleaned up and reformatted
39 * *) The code has been reformatted
40 * *) Several twisty code paths have been fixed so I can understand them.
41 * -Erik, 1 September 2001
43 * *) Made it show major/minor numbers for device nodes
44 * *) Made it show symlink targets
45 * -Erik, 13 September 2001
52 - Add CRC checking code to places marked with XXX.
53 - Add support for other node compression types.
55 - Test with real life images.
56 - Maybe port into bootloader.
62 - Doesn't check CRC checksums.
65 #define PROGRAM_NAME "jffs2reader"
75 #include <sys/types.h>
77 #include <sys/param.h>
79 #include <linux/jffs2.h>
82 #define SCRATCH_SIZE (5*1024*1024)
85 /* FIXME: I am using illicit insider knowledge of
86 * kernel major/minor representation... */
87 #define MAJOR(dev) (((dev)>>8)&0xff)
88 #define MINOR(dev) ((dev)&0xff)
92 #define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0)
93 #define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0)
103 void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *);
104 struct dir *putdir(struct dir *, struct jffs2_raw_dirent *);
105 void printdir(char *o, size_t size, struct dir *d, char *path,
107 void freedir(struct dir *);
109 struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino);
110 struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t,
112 struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t);
113 struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t);
115 struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *,
117 struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *,
120 void lsdir(char *, size_t, char *, int);
121 void catfile(char *, size_t, char *, char *, size_t, size_t *);
123 int main(int, char **);
125 /* writes file node into buffer, to the proper position. */
126 /* reading all valid nodes in version order reconstructs the file. */
135 void putblock(char *b, size_t bsize, size_t * rsize,
136 struct jffs2_raw_inode *n)
138 uLongf dlen = n->dsize;
140 if (n->isize > bsize || (n->offset + dlen) > bsize)
141 errmsg_die("File does not fit into buffer!");
143 if (*rsize < n->isize)
144 bzero(b + *rsize, n->isize - *rsize);
147 case JFFS2_COMPR_ZLIB:
148 uncompress((Bytef *) b + n->offset, &dlen,
149 (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode),
153 case JFFS2_COMPR_NONE:
154 memcpy(b + n->offset,
155 ((char *) n) + sizeof(struct jffs2_raw_inode), dlen);
158 case JFFS2_COMPR_ZERO:
159 bzero(b + n->offset, dlen);
162 /* [DYN]RUBIN support required! */
165 errmsg_die("Unsupported compression method!");
171 /* adds/removes directory node into dir struct. */
172 /* reading all valid nodes in version order reconstructs the directory. */
175 dd - directory struct being processed
178 return value: directory struct value replacing dd
181 struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n)
183 struct dir *o, *d, *p;
189 d = xmalloc(sizeof(struct dir));
191 memcpy(d->name, n->name, n->nsize);
200 if (n->nsize == dd->nsize &&
201 !memcmp(n->name, dd->name, n->nsize)) {
208 if (dd->next == NULL) {
209 dd->next = xmalloc(sizeof(struct dir));
210 dd->next->type = n->type;
211 memcpy(dd->next->name, n->name, n->nsize);
212 dd->next->nsize = n->nsize;
213 dd->next->ino = n->ino;
214 dd->next->next = NULL;
225 if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) {
238 if (n->nsize == dd->nsize &&
239 !memcmp(n->name, dd->name, n->nsize)) {
250 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
251 #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
253 /* The special bits. If set, display SMODE0/1 instead of MODE0/1 */
254 static const mode_t SBIT[] = {
260 /* The 9 mode bits to test */
261 static const mode_t MBIT[] = {
262 S_IRUSR, S_IWUSR, S_IXUSR,
263 S_IRGRP, S_IWGRP, S_IXGRP,
264 S_IROTH, S_IWOTH, S_IXOTH
267 static const char MODE1[] = "rwxrwxrwx";
268 static const char MODE0[] = "---------";
269 static const char SMODE1[] = "..s..s..t";
270 static const char SMODE0[] = "..S..S..T";
273 * Return the standard ls-like mode string from a file mode.
274 * This is static and so is overwritten on each call.
276 const char *mode_string(int mode)
282 buf[0] = TYPECHAR(mode);
283 for (i = 0; i < 9; i++) {
285 buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i];
287 buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i];
292 /* prints contents of directory structure */
298 void printdir(char *o, size_t size, struct dir *d, char *path, int recurse)
303 struct jffs2_raw_inode *ri;
307 if (strlen(path) == 1 && *path == '/')
343 ri = find_raw_inode(o, size, d->ino);
345 warnmsg("bug: raw_inode missing!");
350 filetime = ctime((const time_t *) &(ri->ctime));
351 age = time(NULL) - ri->ctime;
352 printf("%s %-4d %-8d %-8d ", mode_string(ri->mode),
353 1, ri->uid, ri->gid);
354 if ( d->type==DT_BLK || d->type==DT_CHR ) {
357 putblock((char*)&rdev, sizeof(rdev), &devsize, ri);
358 printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev));
360 printf("%9ld ", (long)ri->dsize);
362 d->name[d->nsize]='\0';
363 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
364 /* hh:mm if less than 6 months old */
365 printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m);
367 printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m);
369 if (d->type == DT_LNK) {
372 putblock(symbuf, sizeof(symbuf), &symsize, ri);
374 printf(" -> %s", symbuf);
378 if (d->type == DT_DIR && recurse) {
380 tmp = xmalloc(BUFSIZ);
381 sprintf(tmp, "%s/%s", path, d->name);
382 lsdir(o, size, tmp, recurse); /* Go recursive */
390 /* frees memory used by directory structure */
396 void freedir(struct dir *d)
407 /* collects directory/file nodes in version order. */
411 if zero, collect file, compare ino to inode
412 otherwise, collect directory, compare ino to parent inode
413 o - filesystem image pointer
414 size - size of filesystem image
415 ino - inode to compare against. see f.
417 return value: a jffs2_raw_inode that corresponds the the specified
421 struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino)
424 union jffs2_node_union *n;
425 union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
426 union jffs2_node_union *lr; /* last block position */
427 union jffs2_node_union *mp = NULL; /* minimum position */
429 uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
431 vmin = 0; /* next to read */
432 vmax = ~((uint32_t) 0); /* last to read */
433 vmint = ~((uint32_t) 0);
434 vmaxt = 0; /* found maximum */
435 vcur = 0; /* XXX what is smallest version number used? */
436 /* too low version number can easily result excess log rereading */
438 n = (union jffs2_node_union *) o;
442 while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
445 if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
446 if (n->u.nodetype == JFFS2_NODETYPE_INODE &&
447 n->i.ino == ino && (v = n->i.version) > vcur) {
461 ((char *) n) += ((n->u.totlen + 3) & ~3);
463 n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
465 if (lr == n) { /* whole loop since last read */
468 vmint = ~((uint32_t) 0);
470 if (vcur < vmax && vcur < vmin)
473 } while (vcur < vmax);
478 /* collects dir struct for selected inode */
481 o - filesystem image pointer
482 size - size of filesystem image
483 pino - inode of the specified directory
484 d - input directory structure
486 return value: result directory structure, replaces d.
489 struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d)
492 union jffs2_node_union *n;
493 union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
494 union jffs2_node_union *lr; /* last block position */
495 union jffs2_node_union *mp = NULL; /* minimum position */
497 uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
499 vmin = 0; /* next to read */
500 vmax = ~((uint32_t) 0); /* last to read */
501 vmint = ~((uint32_t) 0);
502 vmaxt = 0; /* found maximum */
503 vcur = 0; /* XXX what is smallest version number used? */
504 /* too low version number can easily result excess log rereading */
506 n = (union jffs2_node_union *) o;
510 while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
513 if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
514 if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
515 n->d.pino == ino && (v = n->d.version) > vcur) {
525 if (v == (vcur + 1)) {
526 d = putdir(d, &(n->d));
530 vmint = ~((uint32_t) 0);
534 ((char *) n) += ((n->u.totlen + 3) & ~3);
536 n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
538 if (lr == n) { /* whole loop since last read */
541 vmint = ~((uint32_t) 0);
543 if (vcur < vmax && vcur < vmin) {
544 d = putdir(d, &(mp->d));
547 (union jffs2_node_union *) (((char *) mp) +
548 ((mp->u.totlen + 3) & ~3));
553 } while (vcur < vmax);
560 /* resolve dirent based on criteria */
563 o - filesystem image pointer
564 size - size of filesystem image
565 ino - if zero, ignore,
566 otherwise compare against dirent inode
567 pino - if zero, ingore,
568 otherwise compare against parent inode
569 and use name and nsize as extra criteria
570 name - name of wanted dirent, used if pino!=0
571 nsize - length of name of wanted dirent, used if pino!=0
573 return value: pointer to relevant dirent structure in
574 filesystem image or NULL
577 struct jffs2_raw_dirent *resolvedirent(char *o, size_t size,
578 uint32_t ino, uint32_t pino,
579 char *name, uint8_t nsize)
582 union jffs2_node_union *n;
583 union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
585 struct jffs2_raw_dirent *dd = NULL;
589 if (!pino && ino <= 1)
594 n = (union jffs2_node_union *) o;
597 while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
600 if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
601 if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
602 (!ino || n->d.ino == ino) &&
603 (v = n->d.version) > vmax &&
604 (!pino || (n->d.pino == pino &&
605 nsize == n->d.nsize &&
606 !memcmp(name, n->d.name, nsize)))) {
615 ((char *) n) += ((n->u.totlen + 3) & ~3);
621 /* resolve name under certain parent inode to dirent */
624 o - filesystem image pointer
625 size - size of filesystem image
626 pino - requested parent inode
627 name - name of wanted dirent
628 nsize - length of name of wanted dirent
630 return value: pointer to relevant dirent structure in
631 filesystem image or NULL
634 struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino,
635 char *name, uint8_t nsize)
637 return resolvedirent(o, size, 0, pino, name, nsize);
640 /* resolve inode to dirent */
643 o - filesystem image pointer
644 size - size of filesystem image
645 ino - compare against dirent inode
647 return value: pointer to relevant dirent structure in
648 filesystem image or NULL
651 struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino)
653 return resolvedirent(o, size, ino, 0, NULL, 0);
656 /* resolve slash-style path into dirent and inode.
657 slash as first byte marks absolute path (root=inode 1).
658 . and .. are resolved properly, and symlinks are followed.
662 o - filesystem image pointer
663 size - size of filesystem image
664 ino - root inode, used if path is relative
665 p - path to be resolved
666 inos - result inode, zero if failure
667 recc - recursion count, to detect symlink loops
669 return value: pointer to dirent struct in file system image.
670 note that root directory doesn't have dirent struct
671 (return value is NULL), but it has inode (*inos=1)
674 struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino,
675 char *p, uint32_t * inos, int recc)
677 struct jffs2_raw_dirent *dir = NULL;
690 /* probably symlink loop */
695 pp = path = strdup(p);
703 dir = resolveinode(o, size, ino);
705 ino = DIRENT_INO(dir);
710 while (ino && next != NULL && next[1] != 0 && d) {
712 next = strchr(path, '/');
717 if (*path == '.' && path[1] == 0)
719 if (*path == '.' && path[1] == '.' && path[2] == 0) {
720 if (DIRENT_PINO(dir) == 1) {
724 dir = resolveinode(o, size, DIRENT_PINO(dir));
725 ino = DIRENT_INO(dir);
731 dir = resolvename(o, size, ino, path, (uint8_t) strlen(path));
733 if (DIRENT_INO(dir) == 0 ||
735 !(dir->type == DT_DIR || dir->type == DT_LNK))) {
743 if (dir->type == DT_LNK) {
744 struct jffs2_raw_inode *ri;
745 ri = find_raw_inode(o, size, DIRENT_INO(dir));
746 putblock(symbuf, sizeof(symbuf), &symsize, ri);
752 dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc);
754 if (dir != NULL && next != NULL &&
755 !(dir->type == DT_DIR || dir->type == DT_LNK)) {
763 ino = DIRENT_INO(dir);
773 /* resolve slash-style path into dirent and inode.
774 slash as first byte marks absolute path (root=inode 1).
775 . and .. are resolved properly, and symlinks are followed.
779 o - filesystem image pointer
780 size - size of filesystem image
781 ino - root inode, used if path is relative
782 p - path to be resolved
783 inos - result inode, zero if failure
785 return value: pointer to dirent struct in file system image.
786 note that root directory doesn't have dirent struct
787 (return value is NULL), but it has inode (*inos=1)
790 struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino,
791 char *p, uint32_t * inos)
793 return resolvepath0(o, size, ino, p, inos, 0);
796 /* lists files on directory specified by path */
799 o - filesystem image pointer
800 size - size of filesystem image
801 p - path to be resolved
804 void lsdir(char *o, size_t size, char *path, int recurse)
806 struct jffs2_raw_dirent *dd;
807 struct dir *d = NULL;
811 dd = resolvepath(o, size, 1, path, &ino);
814 (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR))
815 errmsg_die("%s: No such file or directory", path);
817 d = collectdir(o, size, ino, d);
818 printdir(o, size, d, path, recurse);
822 /* writes file specified by path to the buffer */
825 o - filesystem image pointer
826 size - size of filesystem image
827 p - path to be resolved
829 bsize - file buffer size
830 rsize - file result size
833 void catfile(char *o, size_t size, char *path, char *b, size_t bsize,
836 struct jffs2_raw_dirent *dd;
837 struct jffs2_raw_inode *ri;
840 dd = resolvepath(o, size, 1, path, &ino);
843 errmsg_die("%s: No such file or directory", path);
845 if (dd == NULL || dd->type != DT_REG)
846 errmsg_die("%s: Not a regular file", path);
848 ri = find_raw_inode(o, size, ino);
849 putblock(b, bsize, rsize, ri);
856 int main(int argc, char **argv)
858 int fd, opt, recurse = 0;
861 char *scratch, *dir = NULL, *file = NULL;
866 while ((opt = getopt(argc, argv, "rd:f:")) > 0) {
879 "Usage: %s <image> [-d|-f] < path >\n",
885 fd = open(argv[optind], O_RDONLY);
887 sys_errmsg_die("%s", argv[optind]);
890 sys_errmsg_die("%s", argv[optind]);
892 buf = xmalloc((size_t) st.st_size);
894 if (read(fd, buf, st.st_size) != (ssize_t) st.st_size)
895 sys_errmsg_die("%s", argv[optind]);
898 lsdir(buf, st.st_size, dir, recurse);
901 scratch = xmalloc(SCRATCH_SIZE);
903 catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize);
908 lsdir(buf, st.st_size, "/", 1);