FreeCalypso > hg > freecalypso-reveng
view mpffs/common.c @ 215:d69f7512e3c1
Pirelli: documented and verified the checksum scheme used for the factory block
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 25 Dec 2016 23:48:16 +0000 |
parents | 9bc7f0e03fa8 |
children |
line wrap: on
line source
/* * This module contains the code that will be common between mpffs-cat, * mpffs-ls and mpffs-xtr. */ #include <sys/types.h> #include <sys/file.h> #include <sys/stat.h> #include <endian.h> #include <ctype.h> #include <stdio.h> #include <string.h> #include <strings.h> #include <stdlib.h> #include <unistd.h> #include "types.h" #include "struct.h" u8 mpffs_header[6] = {'F', 'f', 's', '#', 0x10, 0x02}; u8 blank_flash_line[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; char *imgfile; u32 eraseblk_size; int total_blocks; u32 total_ffs_size; u8 *image, *indexblk; int index_blk_num = -1, root_node_no; struct objinfo root; int verbose; /* * The following function is used to verify that the specified or computed * flash erase block size is a power of 2. */ count_ones(word) u32 word; { int count; for (count = 0; word; word >>= 1) count += word & 1; return count; } eraseblk_size_reasonable() { if (count_ones(eraseblk_size) != 1 || eraseblk_size < 16384 || eraseblk_size > 0x100000) { fprintf(stderr, "0x%lx is an unreasonable erase block size\n", (u_long) eraseblk_size); exit(1); } } parse_cmdline_options(argc, argv) char **argv; { extern char *optarg; int c; while ((c = getopt(argc, argv, "a:e:mn:pr:v")) != EOF) switch (c) { case 'a': index_blk_num = atoi(optarg); continue; case 'e': eraseblk_size = strtoul(optarg, 0, 0); eraseblk_size_reasonable(); continue; case 'm': /* "moko" */ eraseblk_size = 0x10000; total_blocks = 7; continue; case 'n': total_blocks = atoi(optarg); if (total_blocks < 1) { fprintf(stderr, "invalid -n value\n"); exit(1); } continue; case 'p': /* Pirelli */ eraseblk_size = 0x40000; total_blocks = 18; continue; case 'r': root_node_no = atoi(optarg); continue; case 'v': verbose++; continue; default: usage(); exit(1); } return(0); } read_img_file() { int fd; struct stat st; fd = open(imgfile, O_RDONLY); if (fd < 0) { perror(imgfile); exit(1); } fstat(fd, &st); if (!S_ISREG(st.st_mode)) { fprintf(stderr, "%s is not a regular file\n", imgfile); exit(1); } if (!eraseblk_size && !total_blocks) { switch (st.st_size) { case 0x70000: /* assume Closedmoko */ eraseblk_size = 0x10000; total_blocks = 7; break; case 0x480000: /* assume Pirelli */ eraseblk_size = 0x40000; total_blocks = 18; break; default: noauto: fprintf(stderr, "cannot intuit the flash parameters of %s: use -e and -n options\n", imgfile); exit(1); } } else if (!total_blocks) { total_blocks = st.st_size / eraseblk_size; if (!total_blocks || total_blocks * eraseblk_size != st.st_size) goto noauto; } else if (!eraseblk_size) { if (total_blocks * 0x10000 == st.st_size) eraseblk_size = 0x10000; else if (total_blocks * 0x40000 == st.st_size) eraseblk_size = 0x40000; else goto noauto; } total_ffs_size = eraseblk_size * total_blocks; if (st.st_size < total_ffs_size) { fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile, total_ffs_size); exit(1); } image = malloc(total_ffs_size); if (!image) { perror("malloc"); exit(1); } read(fd, image, total_ffs_size); close(fd); } find_index_block() { int i, abcnt; u8 *ptr; if (index_blk_num >= 0) { if (index_blk_num >= total_blocks) { fprintf(stderr, "invalid block # given with the -a option\n"); exit(1); } ptr = image + index_blk_num * eraseblk_size; if (bcmp(ptr, mpffs_header, 6) || ptr[8] != 0xAB) { fprintf(stderr, "no MPFFS index found at the specified block #%d\n", index_blk_num); exit(1); } indexblk = ptr; return(0); } abcnt = 0; for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) { if (bcmp(ptr, mpffs_header, 6)) { fprintf(stderr, "warning: no MPFFS signature in erase block #%d (offset %x)\n", i, ptr - image); continue; } switch (ptr[8]) { case 0xAB: if (verbose) printf( "Found AB index in erase block #%d (offset %x)\n", i, ptr - image); index_blk_num = i; indexblk = ptr; abcnt++; continue; case 0xBD: case 0xBF: continue; } fprintf(stderr, "warning: non-understood block type %02X at offset %x\n", ptr[8], ptr - image); } if (!indexblk) { fprintf(stderr, "could not find an MPFFS index block in %s\n", imgfile); exit(1); } if (abcnt > 1) { fprintf(stderr, "found more than one AB block; use -a\n"); exit(1); } return(0); } get_index_entry(oi) struct objinfo *oi; { struct mpffs_index *le; if (oi->entryno >= (eraseblk_size >> 4)) { fprintf(stderr, "error: index block pointer %x past the erase block size!\n", oi->entryno); exit(1); } le = (struct mpffs_index *) indexblk + oi->entryno; oi->idxrec = le; oi->len = le16toh(le->len); oi->type = le->type; oi->descend = le16toh(le->descend); oi->sibling = le16toh(le->sibling); return(0); } validate_chunk(oi) struct objinfo *oi; { u32 dptr; if (oi->len & 0xF || !oi->len) { fprintf(stderr, "index entry #%x: invalid chunk length\n", oi->entryno); exit(1); } dptr = le32toh(oi->idxrec->dataptr); if (dptr > 0x0FFFFFFF) { invdptr: fprintf(stderr, "index entry #%x: invalid data pointer\n", oi->entryno); exit(1); } dptr <<= 4; if (dptr > total_ffs_size - oi->len) goto invdptr; oi->offset = dptr; oi->dataptr = image + dptr; return(0); } validate_obj_name(oi) struct objinfo *oi; { u8 *cp; int cnt; for (cp = oi->dataptr, cnt = 0; ; cp++, cnt++) { if (cnt >= oi->len) { fprintf(stderr, "object at index %x: name expected at %x: length overrun\n", oi->entryno, oi->offset); exit(1); } if (!*cp) break; if (*cp < '!' || *cp > '~') { fprintf(stderr, "object at index %x: name expected at %x: bad character\n", oi->entryno, oi->offset); exit(1); } } if (!cnt) { fprintf(stderr, "object at index %x: name expected at %x: null string\n", oi->entryno, oi->offset); exit(1); } return(0); } u8 * find_end_of_chunk(ch) struct objinfo *ch; { u8 *p; int i; p = ch->dataptr + ch->len; for (i = 1; i <= 16; i++) { if (!p[-i]) return(p - i); if (p[-1] != 0xFF) break; } fprintf(stderr, "chunk starting at %x (index entry %x): no valid termination found\n", ch->offset, ch->entryno); exit(1); } size_head_chunk(ch, chi) struct objinfo *ch; struct chunkinfo *chi; { chi->start = (u8 *) index((char *)ch->dataptr, '\0') + 1; chi->end = find_end_of_chunk(ch); if (chi->start >= chi->end) { chi->len = 0; return(0); } else { chi->len = chi->end - chi->start; return(1); } } size_extra_chunk(ch, chi) struct objinfo *ch; struct chunkinfo *chi; { chi->start = ch->dataptr; chi->end = find_end_of_chunk(ch); chi->len = chi->end - chi->start; } iterate_extra_chunks(first_extra_chunk, callback, callback_data) void (*callback)(); u_long callback_data; { int ent; struct objinfo obj; for (ent = first_extra_chunk; ent != 0xFFFF; ent = obj.descend) { loop: obj.entryno = ent; get_index_entry(&obj); switch (obj.type) { case 0xF4: validate_chunk(&obj); callback(&obj, callback_data); continue; case 0x00: if (obj.sibling == 0xFFFF) { fprintf(stderr, "file continuation object at index %x: marked deleted, but no sibling\n", ent); exit(1); } ent = obj.sibling; goto loop; default: fprintf(stderr, "file continuation object at index %x: unexpected type %02X\n", ent, obj.type); exit(1); } } } find_root_node() { struct objinfo obj; u16 lim; lim = (eraseblk_size >> 4) - 1; if (root_node_no) { if (root_node_no < 1 || root_node_no > lim) { fprintf(stderr, "root node # given with -r is invalid\n"); exit(1); } return(1); } for (obj.entryno = 1; obj.entryno <= lim; obj.entryno++) { get_index_entry(&obj); if (!bcmp(obj.idxrec, blank_flash_line, 16)) break; if (obj.type != 0xF2) continue; validate_chunk(&obj); validate_obj_name(&obj); if (*obj.dataptr != '/') continue; root_node_no = obj.entryno; if (verbose) printf("Found root node at index #%x\n", root_node_no); return(0); } fprintf(stderr, "error: no root node found (try -r)\n"); exit(1); } validate_root_node() { root.entryno = root_node_no; get_index_entry(&root); validate_chunk(&root); validate_obj_name(&root); if (verbose) printf("Root node name: %s\n", (char *)root.dataptr); if (root.type != 0xF2) { fprintf(stderr, "error: index entry #%x (expected root dir) is not a directory\n", root_node_no); exit(1); } if (root.sibling != 0xFFFF) fprintf(stderr, "warning: root entry has a non-nil sibling pointer\n"); return(0); } preliminaries() { read_img_file(); find_index_block(); find_root_node(); validate_root_node(); }