360 lines
8.4 KiB
C
360 lines
8.4 KiB
C
|
/*
|
||
|
* Dump JFFS filesystem.
|
||
|
* Useful when it buggers up.
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <dirent.h>
|
||
|
#include <unistd.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <asm/byteorder.h>
|
||
|
|
||
|
#include "common.h"
|
||
|
|
||
|
#define BLOCK_SIZE 1024
|
||
|
#define JFFS_MAGIC 0x34383931 /* "1984" */
|
||
|
#define JFFS_MAX_NAME_LEN 256
|
||
|
#define JFFS_MIN_INO 1
|
||
|
#define JFFS_TRACE_INDENT 4
|
||
|
#define JFFS_ALIGN_SIZE 4
|
||
|
#define MAX_CHUNK_SIZE 32768
|
||
|
|
||
|
/* How many padding bytes should be inserted between two chunks of data
|
||
|
on the flash? */
|
||
|
#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \
|
||
|
- ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \
|
||
|
% JFFS_ALIGN_SIZE)
|
||
|
|
||
|
#define JFFS_EMPTY_BITMASK 0xffffffff
|
||
|
#define JFFS_MAGIC_BITMASK 0x34383931
|
||
|
#define JFFS_DIRTY_BITMASK 0x00000000
|
||
|
|
||
|
struct jffs_raw_inode
|
||
|
{
|
||
|
uint32_t magic; /* A constant magic number. */
|
||
|
uint32_t ino; /* Inode number. */
|
||
|
uint32_t pino; /* Parent's inode number. */
|
||
|
uint32_t version; /* Version number. */
|
||
|
uint32_t mode; /* file_type, mode */
|
||
|
uint16_t uid;
|
||
|
uint16_t gid;
|
||
|
uint32_t atime;
|
||
|
uint32_t mtime;
|
||
|
uint32_t ctime;
|
||
|
uint32_t offset; /* Where to begin to write. */
|
||
|
uint32_t dsize; /* Size of the file data. */
|
||
|
uint32_t rsize; /* How much are going to be replaced? */
|
||
|
uint8_t nsize; /* Name length. */
|
||
|
uint8_t nlink; /* Number of links. */
|
||
|
uint8_t spare : 6; /* For future use. */
|
||
|
uint8_t rename : 1; /* Is this a special rename? */
|
||
|
uint8_t deleted : 1; /* Has this file been deleted? */
|
||
|
uint8_t accurate; /* The inode is obsolete if accurate == 0. */
|
||
|
uint32_t dchksum; /* Checksum for the data. */
|
||
|
uint16_t nchksum; /* Checksum for the name. */
|
||
|
uint16_t chksum; /* Checksum for the raw_inode. */
|
||
|
};
|
||
|
|
||
|
|
||
|
struct jffs_file
|
||
|
{
|
||
|
struct jffs_raw_inode inode;
|
||
|
char *name;
|
||
|
unsigned char *data;
|
||
|
};
|
||
|
|
||
|
|
||
|
char *root_directory_name = NULL;
|
||
|
int fs_pos = 0;
|
||
|
int verbose = 0;
|
||
|
|
||
|
#define ENDIAN_HOST 0
|
||
|
#define ENDIAN_BIG 1
|
||
|
#define ENDIAN_LITTLE 2
|
||
|
int endian = ENDIAN_HOST;
|
||
|
|
||
|
static uint32_t jffs_checksum(void *data, int size);
|
||
|
void jffs_print_trace(const char *path, int depth);
|
||
|
int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path,
|
||
|
int depth);
|
||
|
void write_file(struct jffs_file *f, FILE *fs, struct stat st);
|
||
|
void read_data(struct jffs_file *f, const char *path, int offset);
|
||
|
int mkfs(FILE *fs, const char *path, int ino, int parent, int depth);
|
||
|
|
||
|
|
||
|
static uint32_t
|
||
|
jffs_checksum(void *data, int size)
|
||
|
{
|
||
|
uint32_t sum = 0;
|
||
|
uint8_t *ptr = (uint8_t *)data;
|
||
|
|
||
|
while (size-- > 0)
|
||
|
{
|
||
|
sum += *ptr++;
|
||
|
}
|
||
|
|
||
|
return sum;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
jffs_print_trace(const char *path, int depth)
|
||
|
{
|
||
|
int path_len = strlen(path);
|
||
|
int out_pos = depth * JFFS_TRACE_INDENT;
|
||
|
int pos = path_len - 1;
|
||
|
char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1);
|
||
|
|
||
|
if (verbose >= 2)
|
||
|
{
|
||
|
fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path);
|
||
|
}
|
||
|
|
||
|
if (!out) {
|
||
|
fprintf(stderr, "jffs_print_trace(): Allocation failed.\n");
|
||
|
fprintf(stderr, " path: \"%s\"\n", path);
|
||
|
fprintf(stderr, "depth: %d\n", depth);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
memset(out, ' ', depth * JFFS_TRACE_INDENT);
|
||
|
|
||
|
if (path[pos] == '/')
|
||
|
{
|
||
|
pos--;
|
||
|
}
|
||
|
while (path[pos] && (path[pos] != '/'))
|
||
|
{
|
||
|
pos--;
|
||
|
}
|
||
|
for (pos++; path[pos] && (path[pos] != '/'); pos++)
|
||
|
{
|
||
|
out[out_pos++] = path[pos];
|
||
|
}
|
||
|
out[out_pos] = '\0';
|
||
|
fprintf(stderr, "%s\n", out);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Print the contents of a raw inode. */
|
||
|
void
|
||
|
jffs_print_raw_inode(struct jffs_raw_inode *raw_inode)
|
||
|
{
|
||
|
fprintf(stdout, "jffs_raw_inode: inode number: %u, version %u\n", raw_inode->ino, raw_inode->version);
|
||
|
fprintf(stdout, "{\n");
|
||
|
fprintf(stdout, " 0x%08x, /* magic */\n", raw_inode->magic);
|
||
|
fprintf(stdout, " 0x%08x, /* ino */\n", raw_inode->ino);
|
||
|
fprintf(stdout, " 0x%08x, /* pino */\n", raw_inode->pino);
|
||
|
fprintf(stdout, " 0x%08x, /* version */\n", raw_inode->version);
|
||
|
fprintf(stdout, " 0x%08x, /* mode */\n", raw_inode->mode);
|
||
|
fprintf(stdout, " 0x%04x, /* uid */\n", raw_inode->uid);
|
||
|
fprintf(stdout, " 0x%04x, /* gid */\n", raw_inode->gid);
|
||
|
fprintf(stdout, " 0x%08x, /* atime */\n", raw_inode->atime);
|
||
|
fprintf(stdout, " 0x%08x, /* mtime */\n", raw_inode->mtime);
|
||
|
fprintf(stdout, " 0x%08x, /* ctime */\n", raw_inode->ctime);
|
||
|
fprintf(stdout, " 0x%08x, /* offset */\n", raw_inode->offset);
|
||
|
fprintf(stdout, " 0x%08x, /* dsize */\n", raw_inode->dsize);
|
||
|
fprintf(stdout, " 0x%08x, /* rsize */\n", raw_inode->rsize);
|
||
|
fprintf(stdout, " 0x%02x, /* nsize */\n", raw_inode->nsize);
|
||
|
fprintf(stdout, " 0x%02x, /* nlink */\n", raw_inode->nlink);
|
||
|
fprintf(stdout, " 0x%02x, /* spare */\n",
|
||
|
raw_inode->spare);
|
||
|
fprintf(stdout, " %u, /* rename */\n",
|
||
|
raw_inode->rename);
|
||
|
fprintf(stdout, " %u, /* deleted */\n",
|
||
|
raw_inode->deleted);
|
||
|
fprintf(stdout, " 0x%02x, /* accurate */\n",
|
||
|
raw_inode->accurate);
|
||
|
fprintf(stdout, " 0x%08x, /* dchksum */\n", raw_inode->dchksum);
|
||
|
fprintf(stdout, " 0x%04x, /* nchksum */\n", raw_inode->nchksum);
|
||
|
fprintf(stdout, " 0x%04x, /* chksum */\n", raw_inode->chksum);
|
||
|
fprintf(stdout, "}\n");
|
||
|
}
|
||
|
|
||
|
static void write_val32(uint32_t *adr, uint32_t val)
|
||
|
{
|
||
|
switch(endian) {
|
||
|
case ENDIAN_HOST:
|
||
|
*adr = val;
|
||
|
break;
|
||
|
case ENDIAN_LITTLE:
|
||
|
*adr = __cpu_to_le32(val);
|
||
|
break;
|
||
|
case ENDIAN_BIG:
|
||
|
*adr = __cpu_to_be32(val);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void write_val16(uint16_t *adr, uint16_t val)
|
||
|
{
|
||
|
switch(endian) {
|
||
|
case ENDIAN_HOST:
|
||
|
*adr = val;
|
||
|
break;
|
||
|
case ENDIAN_LITTLE:
|
||
|
*adr = __cpu_to_le16(val);
|
||
|
break;
|
||
|
case ENDIAN_BIG:
|
||
|
*adr = __cpu_to_be16(val);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint32_t read_val32(uint32_t *adr)
|
||
|
{
|
||
|
uint32_t val;
|
||
|
|
||
|
switch(endian) {
|
||
|
case ENDIAN_HOST:
|
||
|
val = *adr;
|
||
|
break;
|
||
|
case ENDIAN_LITTLE:
|
||
|
val = __le32_to_cpu(*adr);
|
||
|
break;
|
||
|
case ENDIAN_BIG:
|
||
|
val = __be32_to_cpu(*adr);
|
||
|
break;
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static uint16_t read_val16(uint16_t *adr)
|
||
|
{
|
||
|
uint16_t val;
|
||
|
|
||
|
switch(endian) {
|
||
|
case ENDIAN_HOST:
|
||
|
val = *adr;
|
||
|
break;
|
||
|
case ENDIAN_LITTLE:
|
||
|
val = __le16_to_cpu(*adr);
|
||
|
break;
|
||
|
case ENDIAN_BIG:
|
||
|
val = __be16_to_cpu(*adr);
|
||
|
break;
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
int fs;
|
||
|
struct stat sb;
|
||
|
uint32_t wordbuf;
|
||
|
off_t pos = 0;
|
||
|
off_t end;
|
||
|
struct jffs_raw_inode ino;
|
||
|
unsigned char namebuf[4096];
|
||
|
int myino = -1;
|
||
|
|
||
|
if (argc < 2) {
|
||
|
printf("no filesystem given\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
fs = open(argv[1], O_RDONLY);
|
||
|
if (fs < 0) {
|
||
|
perror("open");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (argc > 2) {
|
||
|
myino = atol(argv[2]);
|
||
|
printf("Printing ino #%d\n" , myino);
|
||
|
}
|
||
|
|
||
|
if (fstat(fs, &sb) < 0) {
|
||
|
perror("stat");
|
||
|
close(fs);
|
||
|
exit(1);
|
||
|
}
|
||
|
end = sb.st_size;
|
||
|
|
||
|
while (pos < end) {
|
||
|
if (pread(fs, &wordbuf, 4, pos) < 0) {
|
||
|
perror("pread");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
switch(wordbuf) {
|
||
|
case JFFS_EMPTY_BITMASK:
|
||
|
// printf("0xff started at 0x%lx\n", pos);
|
||
|
for (; pos < end && wordbuf == JFFS_EMPTY_BITMASK; pos += 4) {
|
||
|
if (pread(fs, &wordbuf, 4, pos) < 0) {
|
||
|
perror("pread");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
if (pos < end)
|
||
|
pos -= 4;
|
||
|
// printf("0xff ended at 0x%lx\n", pos);
|
||
|
continue;
|
||
|
|
||
|
case JFFS_DIRTY_BITMASK:
|
||
|
// printf("0x00 started at 0x%lx\n", pos);
|
||
|
for (; pos < end && wordbuf == JFFS_DIRTY_BITMASK; pos += 4) {
|
||
|
if (pread(fs, &wordbuf, 4, pos) < 0) {
|
||
|
perror("pread");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
if (pos < end)
|
||
|
pos -=4;
|
||
|
// printf("0x00 ended at 0x%lx\n", pos);
|
||
|
continue;
|
||
|
|
||
|
default:
|
||
|
printf("Argh. Dirty memory at 0x%lx\n", pos);
|
||
|
// file_hexdump(fs, pos, 128);
|
||
|
for (pos += 4; pos < end; pos += 4) {
|
||
|
if (pread(fs, &wordbuf, 4, pos) < 0) {
|
||
|
perror("pread");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (wordbuf == JFFS_MAGIC_BITMASK)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case JFFS_MAGIC_BITMASK:
|
||
|
if (pread(fs, &ino, sizeof(ino), pos) < 0) {
|
||
|
perror("pread");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (myino == -1 || ino.ino == myino) {
|
||
|
printf("Magic found at 0x%lx\n", pos);
|
||
|
jffs_print_raw_inode(&ino);
|
||
|
}
|
||
|
pos += sizeof(ino);
|
||
|
|
||
|
if (myino == -1 || ino.ino == myino) {
|
||
|
if (ino.nsize) {
|
||
|
if (pread(fs, namebuf, min(ino.nsize, 4095), pos) < 0) {
|
||
|
perror("pread");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (ino.nsize < 4095)
|
||
|
namebuf[ino.nsize] = 0;
|
||
|
else
|
||
|
namebuf[4095] = 0;
|
||
|
printf("Name: \"%s\"\n", namebuf);
|
||
|
} else {
|
||
|
printf("No Name\n");
|
||
|
}
|
||
|
}
|
||
|
pos += (ino.nsize + 3) & ~3;
|
||
|
|
||
|
pos += (ino.dsize + 3) & ~3;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
}
|