919 lines
21 KiB
C
919 lines
21 KiB
C
|
/* 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
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
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.
|
||
|
*/
|
||
|
|
||
|
#define PROGRAM_NAME "jffs2reader"
|
||
|
|
||
|
#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 <dirent.h>
|
||
|
#include <zlib.h>
|
||
|
|
||
|
#include "mtd/jffs2-user.h"
|
||
|
#include "common.h"
|
||
|
|
||
|
#define SCRATCH_SIZE (5*1024*1024)
|
||
|
|
||
|
/* macro to avoid "lvalue required as left operand of assignment" error */
|
||
|
#define ADD_BYTES(p, n) ((p) = (typeof(p))((char *)(p) + (n)))
|
||
|
|
||
|
#define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0)
|
||
|
#define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0)
|
||
|
|
||
|
struct dir {
|
||
|
struct dir *next;
|
||
|
uint8_t type;
|
||
|
uint8_t nsize;
|
||
|
uint32_t ino;
|
||
|
char name[256];
|
||
|
};
|
||
|
|
||
|
int target_endian = __BYTE_ORDER;
|
||
|
|
||
|
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, const char *path,
|
||
|
int recurse, int want_ctime);
|
||
|
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, const char *,
|
||
|
uint32_t *, int);
|
||
|
struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *,
|
||
|
uint32_t *);
|
||
|
|
||
|
void lsdir(char *, size_t, const char *, int, 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 = je32_to_cpu(n->dsize);
|
||
|
|
||
|
if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize)
|
||
|
errmsg_die("File does not fit into buffer!");
|
||
|
|
||
|
if (*rsize < je32_to_cpu(n->isize))
|
||
|
bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize);
|
||
|
|
||
|
switch (n->compr) {
|
||
|
case JFFS2_COMPR_ZLIB:
|
||
|
uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen,
|
||
|
(Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode),
|
||
|
(uLongf) je32_to_cpu(n->csize));
|
||
|
break;
|
||
|
|
||
|
case JFFS2_COMPR_NONE:
|
||
|
memcpy(b + je32_to_cpu(n->offset),
|
||
|
((char *) n) + sizeof(struct jffs2_raw_inode), dlen);
|
||
|
break;
|
||
|
|
||
|
case JFFS2_COMPR_ZERO:
|
||
|
bzero(b + je32_to_cpu(n->offset), dlen);
|
||
|
break;
|
||
|
|
||
|
/* [DYN]RUBIN support required! */
|
||
|
|
||
|
default:
|
||
|
errmsg_die("Unsupported compression method!");
|
||
|
}
|
||
|
|
||
|
*rsize = je32_to_cpu(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 (je32_to_cpu(n->ino)) {
|
||
|
if (dd == NULL) {
|
||
|
d = xmalloc(sizeof(struct dir));
|
||
|
d->type = n->type;
|
||
|
memcpy(d->name, n->name, n->nsize);
|
||
|
d->nsize = n->nsize;
|
||
|
d->ino = je32_to_cpu(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 = je32_to_cpu(n->ino);
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
if (dd->next == NULL) {
|
||
|
dd->next = xmalloc(sizeof(struct dir));
|
||
|
dd->next->type = n->type;
|
||
|
memcpy(dd->next->name, n->name, n->nsize);
|
||
|
dd->next->nsize = n->nsize;
|
||
|
dd->next->ino = je32_to_cpu(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, const char *path, int recurse,
|
||
|
int want_ctime)
|
||
|
{
|
||
|
char m;
|
||
|
char *filetime;
|
||
|
time_t age;
|
||
|
struct jffs2_raw_inode *ri;
|
||
|
jint32_t mode;
|
||
|
|
||
|
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) {
|
||
|
warnmsg("bug: raw_inode missing!");
|
||
|
d = d->next;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
filetime = ctime((const time_t *) &(ri->ctime));
|
||
|
age = time(NULL) - je32_to_cpu(ri->ctime);
|
||
|
mode.v32 = ri->mode.m;
|
||
|
printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)),
|
||
|
1, je16_to_cpu(ri->uid), je16_to_cpu(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 ", major(rdev), minor(rdev));
|
||
|
} else {
|
||
|
printf("%9ld ", (long)je32_to_cpu(ri->dsize));
|
||
|
}
|
||
|
d->name[d->nsize]='\0';
|
||
|
if (want_ctime) {
|
||
|
if (age < 3600L * 24 * 365 / 2 && age > -15 * 60)
|
||
|
/* hh:mm if less than 6 months old */
|
||
|
printf("%6.6s %5.5s ", filetime + 4, filetime + 11);
|
||
|
else
|
||
|
printf("%6.6s %4.4s ", filetime + 4, filetime + 20);
|
||
|
}
|
||
|
printf("%s/%s%c", 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 = xmalloc(BUFSIZ);
|
||
|
sprintf(tmp, "%s/%s", path, d->name);
|
||
|
lsdir(o, size, tmp, recurse, want_ctime); /* 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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK)
|
||
|
ADD_BYTES(n, 4);
|
||
|
|
||
|
if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) {
|
||
|
if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE &&
|
||
|
je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(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));
|
||
|
}
|
||
|
|
||
|
ADD_BYTES(n, ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK)
|
||
|
ADD_BYTES(n, 4);
|
||
|
|
||
|
if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) {
|
||
|
if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT &&
|
||
|
je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ADD_BYTES(n, ((je32_to_cpu(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) +
|
||
|
((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK)
|
||
|
ADD_BYTES(n, 4);
|
||
|
|
||
|
if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) {
|
||
|
if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT &&
|
||
|
(!ino || je32_to_cpu(n->d.ino) == ino) &&
|
||
|
(v = je32_to_cpu(n->d.version)) > vmax &&
|
||
|
(!pino || (je32_to_cpu(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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ADD_BYTES(n, ((je32_to_cpu(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,
|
||
|
const 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 = xstrdup(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,
|
||
|
const 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, const char *path, int recurse, int want_ctime)
|
||
|
{
|
||
|
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))
|
||
|
errmsg_die("%s: No such file or directory", path);
|
||
|
|
||
|
d = collectdir(o, size, ino, d);
|
||
|
printdir(o, size, d, path, recurse, want_ctime);
|
||
|
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)
|
||
|
errmsg_die("%s: No such file or directory", path);
|
||
|
|
||
|
if (dd == NULL || dd->type != DT_REG)
|
||
|
errmsg_die("%s: Not a regular file", path);
|
||
|
|
||
|
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, want_ctime = 0;
|
||
|
struct stat st;
|
||
|
|
||
|
char *scratch, *dir = NULL, *file = NULL;
|
||
|
size_t ssize = 0;
|
||
|
|
||
|
char *buf;
|
||
|
|
||
|
while ((opt = getopt(argc, argv, "rd:f:t")) > 0) {
|
||
|
switch (opt) {
|
||
|
case 'd':
|
||
|
dir = optarg;
|
||
|
break;
|
||
|
case 'f':
|
||
|
file = optarg;
|
||
|
break;
|
||
|
case 'r':
|
||
|
recurse++;
|
||
|
break;
|
||
|
case 't':
|
||
|
want_ctime++;
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(stderr,
|
||
|
"Usage: %s <image> [-d|-f] < path >\n",
|
||
|
PROGRAM_NAME);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fd = open(argv[optind], O_RDONLY);
|
||
|
if (fd == -1)
|
||
|
sys_errmsg_die("%s", argv[optind]);
|
||
|
|
||
|
if (fstat(fd, &st))
|
||
|
sys_errmsg_die("%s", argv[optind]);
|
||
|
|
||
|
buf = xmalloc((size_t) st.st_size);
|
||
|
|
||
|
if (read(fd, buf, st.st_size) != (ssize_t) st.st_size)
|
||
|
sys_errmsg_die("%s", argv[optind]);
|
||
|
|
||
|
if (dir)
|
||
|
lsdir(buf, st.st_size, dir, recurse, want_ctime);
|
||
|
|
||
|
if (file) {
|
||
|
scratch = xmalloc(SCRATCH_SIZE);
|
||
|
|
||
|
catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize);
|
||
|
free(scratch);
|
||
|
}
|
||
|
|
||
|
if (!dir && !file)
|
||
|
lsdir(buf, st.st_size, "/", 1, want_ctime);
|
||
|
|
||
|
|
||
|
free(buf);
|
||
|
exit(EXIT_SUCCESS);
|
||
|
}
|