summaryrefslogtreecommitdiff
path: root/jffs2reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'jffs2reader.c')
-rw-r--r--jffs2reader.c941
1 files changed, 941 insertions, 0 deletions
diff --git a/jffs2reader.c b/jffs2reader.c
new file mode 100644
index 0000000..fdbf581
--- /dev/null
+++ b/jffs2reader.c
@@ -0,0 +1,941 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * jffs2reader v0.0.18 A jffs2 image reader
+ *
+ * Copyright (c) 2001 Jari Kirma <Jari.Kirma@hut.fi>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the author be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose, including commercial applications, and to alter it and
+ * redistribute it freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ *
+ *********
+ * This code was altered September 2001
+ * Changes are Copyright (c) Erik Andersen <andersen@codepoet.org>
+ *
+ * In compliance with (2) above, this is hereby marked as an altered
+ * version of this software. It has been altered as follows:
+ * *) Listing a directory now mimics the behavior of 'ls -l'
+ * *) Support for recursive listing has been added
+ * *) Without options, does a recursive 'ls' on the whole filesystem
+ * *) option parsing now uses getopt()
+ * *) Now uses printf, and error messages go to stderr.
+ * *) The copyright notice has been cleaned up and reformatted
+ * *) The code has been reformatted
+ * *) Several twisty code paths have been fixed so I can understand them.
+ * -Erik, 1 September 2001
+ *
+ * *) Made it show major/minor numbers for device nodes
+ * *) Made it show symlink targets
+ * -Erik, 13 September 2001
+ *
+ * $Id: jffs2reader.c,v 1.6 2005/11/07 11:15:12 gleixner Exp $
+*/
+
+
+/*
+ TODO:
+
+ - Add CRC checking code to places marked with XXX.
+ - Add support for other node compression types.
+
+ - Test with real life images.
+ - Maybe port into bootloader.
+*/
+
+/*
+ BUGS:
+
+ - Doesn't check CRC checksums.
+*/
+
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <zlib.h>
+#include <linux/jffs2.h>
+
+#define SCRATCH_SIZE (5*1024*1024)
+
+#ifndef MAJOR
+/* FIXME: I am using illicit insider knowledge of
+ * kernel major/minor representation... */
+#define MAJOR(dev) (((dev)>>8)&0xff)
+#define MINOR(dev) ((dev)&0xff)
+#endif
+
+
+#define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0)
+#define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0)
+
+struct dir {
+ struct dir *next;
+ uint8_t type;
+ uint8_t nsize;
+ uint32_t ino;
+ char name[256];
+};
+
+void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *);
+struct dir *putdir(struct dir *, struct jffs2_raw_dirent *);
+void printdir(char *o, size_t size, struct dir *d, char *path,
+ int recurse);
+void freedir(struct dir *);
+
+struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino);
+struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t,
+ char *, uint8_t);
+struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t);
+struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t);
+
+struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *,
+ uint32_t *, int);
+struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *,
+ uint32_t *);
+
+void lsdir(char *, size_t, char *, int);
+void catfile(char *, size_t, char *, char *, size_t, size_t *);
+
+int main(int, char **);
+
+/* writes file node into buffer, to the proper position. */
+/* reading all valid nodes in version order reconstructs the file. */
+
+/*
+ b - buffer
+ bsize - buffer size
+ rsize - result size
+ n - node
+*/
+
+void putblock(char *b, size_t bsize, size_t * rsize,
+ struct jffs2_raw_inode *n)
+{
+ uLongf dlen = n->dsize;
+
+ if (n->isize > bsize || (n->offset + dlen) > bsize) {
+ fprintf(stderr, "File does not fit into buffer!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (*rsize < n->isize)
+ bzero(b + *rsize, n->isize - *rsize);
+
+ switch (n->compr) {
+ case JFFS2_COMPR_ZLIB:
+ uncompress((Bytef *) b + n->offset, &dlen,
+ (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode),
+ (uLongf) n->csize);
+ break;
+
+ case JFFS2_COMPR_NONE:
+ memcpy(b + n->offset,
+ ((char *) n) + sizeof(struct jffs2_raw_inode), dlen);
+ break;
+
+ case JFFS2_COMPR_ZERO:
+ bzero(b + n->offset, dlen);
+ break;
+
+ /* [DYN]RUBIN support required! */
+
+ default:
+ fprintf(stderr, "Unsupported compression method!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ *rsize = n->isize;
+}
+
+/* adds/removes directory node into dir struct. */
+/* reading all valid nodes in version order reconstructs the directory. */
+
+/*
+ dd - directory struct being processed
+ n - node
+
+ return value: directory struct value replacing dd
+*/
+
+struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n)
+{
+ struct dir *o, *d, *p;
+
+ o = dd;
+
+ if (n->ino) {
+ if (dd == NULL) {
+ d = malloc(sizeof(struct dir));
+ d->type = n->type;
+ memcpy(d->name, n->name, n->nsize);
+ d->nsize = n->nsize;
+ d->ino = n->ino;
+ d->next = NULL;
+
+ return d;
+ }
+
+ while (1) {
+ if (n->nsize == dd->nsize &&
+ !memcmp(n->name, dd->name, n->nsize)) {
+ dd->type = n->type;
+ dd->ino = n->ino;
+
+ return o;
+ }
+
+ if (dd->next == NULL) {
+ dd->next = malloc(sizeof(struct dir));
+ dd->next->type = n->type;
+ memcpy(dd->next->name, n->name, n->nsize);
+ dd->next->nsize = n->nsize;
+ dd->next->ino = n->ino;
+ dd->next->next = NULL;
+
+ return o;
+ }
+
+ dd = dd->next;
+ }
+ } else {
+ if (dd == NULL)
+ return NULL;
+
+ if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) {
+ d = dd->next;
+ free(dd);
+ return d;
+ }
+
+ while (1) {
+ p = dd;
+ dd = dd->next;
+
+ if (dd == NULL)
+ return o;
+
+ if (n->nsize == dd->nsize &&
+ !memcmp(n->name, dd->name, n->nsize)) {
+ p->next = dd->next;
+ free(dd);
+
+ return o;
+ }
+ }
+ }
+}
+
+
+#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
+#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
+
+/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */
+static const mode_t SBIT[] = {
+ 0, 0, S_ISUID,
+ 0, 0, S_ISGID,
+ 0, 0, S_ISVTX
+};
+
+/* The 9 mode bits to test */
+static const mode_t MBIT[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP,
+ S_IROTH, S_IWOTH, S_IXOTH
+};
+
+static const char MODE1[] = "rwxrwxrwx";
+static const char MODE0[] = "---------";
+static const char SMODE1[] = "..s..s..t";
+static const char SMODE0[] = "..S..S..T";
+
+/*
+ * Return the standard ls-like mode string from a file mode.
+ * This is static and so is overwritten on each call.
+ */
+const char *mode_string(int mode)
+{
+ static char buf[12];
+
+ int i;
+
+ buf[0] = TYPECHAR(mode);
+ for (i = 0; i < 9; i++) {
+ if (mode & SBIT[i])
+ buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i];
+ else
+ buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i];
+ }
+ return buf;
+}
+
+/* prints contents of directory structure */
+
+/*
+ d - dir struct
+*/
+
+void printdir(char *o, size_t size, struct dir *d, char *path, int recurse)
+{
+ char m;
+ char *filetime;
+ time_t age;
+ struct jffs2_raw_inode *ri;
+
+ if (!path)
+ return;
+ if (strlen(path) == 1 && *path == '/')
+ path++;
+
+ while (d != NULL) {
+ switch (d->type) {
+ case DT_REG:
+ m = ' ';
+ break;
+
+ case DT_FIFO:
+ m = '|';
+ break;
+
+ case DT_CHR:
+ m = ' ';
+ break;
+
+ case DT_BLK:
+ m = ' ';
+ break;
+
+ case DT_DIR:
+ m = '/';
+ break;
+
+ case DT_LNK:
+ m = ' ';
+ break;
+
+ case DT_SOCK:
+ m = '=';
+ break;
+
+ default:
+ m = '?';
+ }
+ ri = find_raw_inode(o, size, d->ino);
+ if (!ri) {
+ fprintf(stderr, "bug: raw_inode missing!\n");
+ d = d->next;
+ continue;
+ }
+
+ filetime = ctime((const time_t *) &(ri->ctime));
+ age = time(NULL) - ri->ctime;
+ printf("%s %-4d %-8d %-8d ", mode_string(ri->mode),
+ 1, ri->uid, ri->gid);
+ if ( d->type==DT_BLK || d->type==DT_CHR ) {
+ dev_t rdev;
+ size_t devsize;
+ putblock((char*)&rdev, sizeof(rdev), &devsize, ri);
+ printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev));
+ } else {
+ printf("%9ld ", (long)ri->dsize);
+ }
+ d->name[d->nsize]='\0';
+ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
+ /* hh:mm if less than 6 months old */
+ printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m);
+ } else {
+ printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m);
+ }
+ if (d->type == DT_LNK) {
+ char symbuf[1024];
+ size_t symsize;
+ putblock(symbuf, sizeof(symbuf), &symsize, ri);
+ symbuf[symsize] = 0;
+ printf(" -> %s", symbuf);
+ }
+ printf("\n");
+
+ if (d->type == DT_DIR && recurse) {
+ char *tmp;
+ tmp = malloc(BUFSIZ);
+ if (!tmp) {
+ fprintf(stderr, "memory exhausted\n");
+ exit(EXIT_FAILURE);
+ }
+ sprintf(tmp, "%s/%s", path, d->name);
+ lsdir(o, size, tmp, recurse); /* Go recursive */
+ free(tmp);
+ }
+
+ d = d->next;
+ }
+}
+
+/* frees memory used by directory structure */
+
+/*
+ d - dir struct
+*/
+
+void freedir(struct dir *d)
+{
+ struct dir *t;
+
+ while (d != NULL) {
+ t = d->next;
+ free(d);
+ d = t;
+ }
+}
+
+/* collects directory/file nodes in version order. */
+
+/*
+ f - file flag.
+ if zero, collect file, compare ino to inode
+ otherwise, collect directory, compare ino to parent inode
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - inode to compare against. see f.
+
+ return value: a jffs2_raw_inode that corresponds the the specified
+ inode, or NULL
+*/
+
+struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+ union jffs2_node_union *lr; /* last block position */
+ union jffs2_node_union *mp = NULL; /* minimum position */
+
+ uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
+
+ vmin = 0; /* next to read */
+ vmax = ~((uint32_t) 0); /* last to read */
+ vmint = ~((uint32_t) 0);
+ vmaxt = 0; /* found maximum */
+ vcur = 0; /* XXX what is smallest version number used? */
+ /* too low version number can easily result excess log rereading */
+
+ n = (union jffs2_node_union *) o;
+ lr = n;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_INODE &&
+ n->i.ino == ino && (v = n->i.version) > vcur) {
+ /* XXX crc check */
+
+ if (vmaxt < v)
+ vmaxt = v;
+ if (vmint > v) {
+ vmint = v;
+ mp = n;
+ }
+
+ if (v == (vcur + 1))
+ return (&(n->i));
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
+
+ if (lr == n) { /* whole loop since last read */
+ vmax = vmaxt;
+ vmin = vmint;
+ vmint = ~((uint32_t) 0);
+
+ if (vcur < vmax && vcur < vmin)
+ return (&(mp->i));
+ }
+ } while (vcur < vmax);
+
+ return NULL;
+}
+
+/* collects dir struct for selected inode */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ pino - inode of the specified directory
+ d - input directory structure
+
+ return value: result directory structure, replaces d.
+*/
+
+struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+ union jffs2_node_union *lr; /* last block position */
+ union jffs2_node_union *mp = NULL; /* minimum position */
+
+ uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
+
+ vmin = 0; /* next to read */
+ vmax = ~((uint32_t) 0); /* last to read */
+ vmint = ~((uint32_t) 0);
+ vmaxt = 0; /* found maximum */
+ vcur = 0; /* XXX what is smallest version number used? */
+ /* too low version number can easily result excess log rereading */
+
+ n = (union jffs2_node_union *) o;
+ lr = n;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
+ n->d.pino == ino && (v = n->d.version) > vcur) {
+ /* XXX crc check */
+
+ if (vmaxt < v)
+ vmaxt = v;
+ if (vmint > v) {
+ vmint = v;
+ mp = n;
+ }
+
+ if (v == (vcur + 1)) {
+ d = putdir(d, &(n->d));
+
+ lr = n;
+ vcur++;
+ vmint = ~((uint32_t) 0);
+ }
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
+
+ if (lr == n) { /* whole loop since last read */
+ vmax = vmaxt;
+ vmin = vmint;
+ vmint = ~((uint32_t) 0);
+
+ if (vcur < vmax && vcur < vmin) {
+ d = putdir(d, &(mp->d));
+
+ lr = n =
+ (union jffs2_node_union *) (((char *) mp) +
+ ((mp->u.totlen + 3) & ~3));
+
+ vcur = vmin;
+ }
+ }
+ } while (vcur < vmax);
+
+ return d;
+}
+
+
+
+/* resolve dirent based on criteria */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - if zero, ignore,
+ otherwise compare against dirent inode
+ pino - if zero, ingore,
+ otherwise compare against parent inode
+ and use name and nsize as extra criteria
+ name - name of wanted dirent, used if pino!=0
+ nsize - length of name of wanted dirent, used if pino!=0
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+*/
+
+struct jffs2_raw_dirent *resolvedirent(char *o, size_t size,
+ uint32_t ino, uint32_t pino,
+ char *name, uint8_t nsize)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+
+ struct jffs2_raw_dirent *dd = NULL;
+
+ uint32_t vmax, v;
+
+ if (!pino && ino <= 1)
+ return dd;
+
+ vmax = 0;
+
+ n = (union jffs2_node_union *) o;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
+ (!ino || n->d.ino == ino) &&
+ (v = n->d.version) > vmax &&
+ (!pino || (n->d.pino == pino &&
+ nsize == n->d.nsize &&
+ !memcmp(name, n->d.name, nsize)))) {
+ /* XXX crc check */
+
+ if (vmax < v) {
+ vmax = v;
+ dd = &(n->d);
+ }
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ return dd;
+ } while (1);
+}
+
+/* resolve name under certain parent inode to dirent */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ pino - requested parent inode
+ name - name of wanted dirent
+ nsize - length of name of wanted dirent
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+*/
+
+struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino,
+ char *name, uint8_t nsize)
+{
+ return resolvedirent(o, size, 0, pino, name, nsize);
+}
+
+/* resolve inode to dirent */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - compare against dirent inode
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+*/
+
+struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino)
+{
+ return resolvedirent(o, size, ino, 0, NULL, 0);
+}
+
+/* resolve slash-style path into dirent and inode.
+ slash as first byte marks absolute path (root=inode 1).
+ . and .. are resolved properly, and symlinks are followed.
+*/
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - root inode, used if path is relative
+ p - path to be resolved
+ inos - result inode, zero if failure
+ recc - recursion count, to detect symlink loops
+
+ return value: pointer to dirent struct in file system image.
+ note that root directory doesn't have dirent struct
+ (return value is NULL), but it has inode (*inos=1)
+*/
+
+struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino,
+ char *p, uint32_t * inos, int recc)
+{
+ struct jffs2_raw_dirent *dir = NULL;
+
+ int d = 1;
+ uint32_t tino;
+
+ char *next;
+
+ char *path, *pp;
+
+ char symbuf[1024];
+ size_t symsize;
+
+ if (recc > 16) {
+ /* probably symlink loop */
+ *inos = 0;
+ return NULL;
+ }
+
+ pp = path = strdup(p);
+
+ if (*path == '/') {
+ path++;
+ ino = 1;
+ }
+
+ if (ino > 1) {
+ dir = resolveinode(o, size, ino);
+
+ ino = DIRENT_INO(dir);
+ }
+
+ next = path - 1;
+
+ while (ino && next != NULL && next[1] != 0 && d) {
+ path = next + 1;
+ next = strchr(path, '/');
+
+ if (next != NULL)
+ *next = 0;
+
+ if (*path == '.' && path[1] == 0)
+ continue;
+ if (*path == '.' && path[1] == '.' && path[2] == 0) {
+ if (DIRENT_PINO(dir) == 1) {
+ ino = 1;
+ dir = NULL;
+ } else {
+ dir = resolveinode(o, size, DIRENT_PINO(dir));
+ ino = DIRENT_INO(dir);
+ }
+
+ continue;
+ }
+
+ dir = resolvename(o, size, ino, path, (uint8_t) strlen(path));
+
+ if (DIRENT_INO(dir) == 0 ||
+ (next != NULL &&
+ !(dir->type == DT_DIR || dir->type == DT_LNK))) {
+ free(pp);
+
+ *inos = 0;
+
+ return NULL;
+ }
+
+ if (dir->type == DT_LNK) {
+ struct jffs2_raw_inode *ri;
+ ri = find_raw_inode(o, size, DIRENT_INO(dir));
+ putblock(symbuf, sizeof(symbuf), &symsize, ri);
+ symbuf[symsize] = 0;
+
+ tino = ino;
+ ino = 0;
+
+ dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc);
+
+ if (dir != NULL && next != NULL &&
+ !(dir->type == DT_DIR || dir->type == DT_LNK)) {
+ free(pp);
+
+ *inos = 0;
+ return NULL;
+ }
+ }
+ if (dir != NULL)
+ ino = DIRENT_INO(dir);
+ }
+
+ free(pp);
+
+ *inos = ino;
+
+ return dir;
+}
+
+/* resolve slash-style path into dirent and inode.
+ slash as first byte marks absolute path (root=inode 1).
+ . and .. are resolved properly, and symlinks are followed.
+*/
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - root inode, used if path is relative
+ p - path to be resolved
+ inos - result inode, zero if failure
+
+ return value: pointer to dirent struct in file system image.
+ note that root directory doesn't have dirent struct
+ (return value is NULL), but it has inode (*inos=1)
+*/
+
+struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino,
+ char *p, uint32_t * inos)
+{
+ return resolvepath0(o, size, ino, p, inos, 0);
+}
+
+/* lists files on directory specified by path */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ p - path to be resolved
+*/
+
+void lsdir(char *o, size_t size, char *path, int recurse)
+{
+ struct jffs2_raw_dirent *dd;
+ struct dir *d = NULL;
+
+ uint32_t ino;
+
+ dd = resolvepath(o, size, 1, path, &ino);
+
+ if (ino == 0 ||
+ (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) {
+ fprintf(stderr, "jffs2reader: %s: No such file or directory\n",
+ path);
+ exit(EXIT_FAILURE);
+ }
+
+ d = collectdir(o, size, ino, d);
+ printdir(o, size, d, path, recurse);
+ freedir(d);
+}
+
+/* writes file specified by path to the buffer */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ p - path to be resolved
+ b - file buffer
+ bsize - file buffer size
+ rsize - file result size
+*/
+
+void catfile(char *o, size_t size, char *path, char *b, size_t bsize,
+ size_t * rsize)
+{
+ struct jffs2_raw_dirent *dd;
+ struct jffs2_raw_inode *ri;
+ uint32_t ino;
+
+ dd = resolvepath(o, size, 1, path, &ino);
+
+ if (ino == 0) {
+ fprintf(stderr, "%s: No such file or directory\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (dd == NULL || dd->type != DT_REG) {
+ fprintf(stderr, "%s: Not a regular file\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ ri = find_raw_inode(o, size, ino);
+ putblock(b, bsize, rsize, ri);
+
+ write(1, b, *rsize);
+}
+
+/* usage example */
+
+int main(int argc, char **argv)
+{
+ int fd, opt, recurse = 0;
+ struct stat st;
+
+ char *scratch, *dir = NULL, *file = NULL;
+ size_t ssize = 0;
+
+ char *buf;
+
+ while ((opt = getopt(argc, argv, "rd:f:")) > 0) {
+ switch (opt) {
+ case 'd':
+ dir = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'r':
+ recurse++;
+ break;
+ default:
+ fprintf(stderr,
+ "Usage: jffs2reader <image> [-d|-f] < path > \n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fd = open(argv[optind], O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(2);
+ }
+
+ if (fstat(fd, &st)) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(3);
+ }
+
+ buf = malloc((size_t) st.st_size);
+ if (buf == NULL) {
+ fprintf(stderr, "%s: memory exhausted\n", argv[optind]);
+ exit(4);
+ }
+
+ if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(5);
+ }
+
+ if (dir)
+ lsdir(buf, st.st_size, dir, recurse);
+
+ if (file) {
+ scratch = malloc(SCRATCH_SIZE);
+ if (scratch == NULL) {
+ fprintf(stderr, "%s: memory exhausted\n", argv[optind]);
+ exit(6);
+ }
+
+ catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize);
+ free(scratch);
+ }
+
+ if (!dir && !file)
+ lsdir(buf, st.st_size, "/", 1);
+
+
+ free(buf);
+ exit(EXIT_SUCCESS);
+}