/* * Calvaria - Maemo CAL partition variable access tool. * * Copyright (c) 2011 Michael Buesch * * Licensed under the GNU General Public License * version 2 or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #define _packed __attribute__((__packed__)) typedef uint16_t le16_t; typedef uint32_t le32_t; struct header { char magic[4]; /* Magic sequence */ uint8_t type; /* Type number */ uint8_t index; /* Index number */ le16_t flags; /* Flags */ char name[16]; /* Human readable section name */ le32_t length; /* Payload length */ le32_t datasum; /* Data CRC32 checksum */ le32_t hdrsum; /* Header CRC32 checksum */ } _packed; #define HDR_MAGIC "ConF" static char toAscii(char c) { if (c >= 32 && c <= 126) return c; return '.'; } static void dump(FILE *outstream, const char *buf, size_t size) { size_t i, ascii_cnt = 0; char ascii[17] = { 0, }; for (i = 0; i < size; i++) { if (i % 16 == 0) { if (i != 0) { fprintf(outstream, " |%s|\n", ascii); ascii[0] = 0; ascii_cnt = 0; } fprintf(outstream, "[%04X]: ", (unsigned int)i); } fprintf(outstream, " %02X", buf[i]); ascii[ascii_cnt] = toAscii(buf[i]); ascii[ascii_cnt + 1] = 0; ascii_cnt++; } if (ascii[0]) { if (size % 16) { for (i = 0; i < 16 - (size % 16); i++) fprintf(outstream, " "); } fprintf(outstream, " |%s|\n", ascii); } fprintf(outstream, "\n"); } static uint32_t crc32(uint32_t crc, const void *_data, size_t size) { const uint8_t *data = _data; uint8_t value; unsigned int bit; size_t i; const uint32_t poly = 0xEDB88320; for (i = 0; i < size; i++) { value = data[i]; for (bit = 8; bit; bit--) { if ((crc & 1) != (value & 1)) crc = (crc >> 1) ^ poly; else crc >>= 1; value >>= 1; } } return crc; } static inline uint16_t le16_to_cpu(le16_t x) { uint8_t *bytes = (uint8_t *)&x; uint16_t ret; ret = bytes[0]; ret |= (uint16_t)(bytes[1]) << 8; return ret; } static inline uint32_t le32_to_cpu(le32_t x) { uint8_t *bytes = (uint8_t *)&x; uint32_t ret; ret = bytes[0]; ret |= (uint32_t)(bytes[1]) << 8; ret |= (uint32_t)(bytes[2]) << 16; ret |= (uint32_t)(bytes[3]) << 24; return ret; } static int is_header(void *data, size_t size) { struct header *hdr = data; if (size < sizeof(struct header)) return 0; if (memcmp(hdr->magic, HDR_MAGIC, sizeof(hdr->magic)) != 0) return 0; return 1; } static int dump_section(const struct header *hdr, const void *payload, size_t payload_len, int dump_payload, FILE *outstream) { char name[sizeof(hdr->name) + 1] = { 0, }; int hdrsum_ok, datasum_ok; memcpy(name, hdr->name, sizeof(hdr->name)); hdrsum_ok = (crc32(0, hdr, sizeof(*hdr) - 4) == le32_to_cpu(hdr->hdrsum)); datasum_ok = (crc32(0, payload, payload_len) == le32_to_cpu(hdr->datasum)); fprintf(outstream, "Section: %s\n", name); fprintf(outstream, "Type: %u (0x%X)\n", hdr->type, hdr->type); fprintf(outstream, "Index: %u (0x%X)\n", hdr->index, hdr->index); fprintf(outstream, "Flags: 0x%04X\n", le16_to_cpu(hdr->flags)); fprintf(outstream, "Length: %u (0x%X)\n", le32_to_cpu(hdr->length), le32_to_cpu(hdr->length)); fprintf(outstream, "Data CRC32: 0x%08X (%s)\n", le32_to_cpu(hdr->datasum), datasum_ok ? "Ok" : "MISMATCH"); fprintf(outstream, "Header CRC32: 0x%08X (%s)\n", le32_to_cpu(hdr->hdrsum), hdrsum_ok ? "Ok" : "MISMATCH"); if (dump_payload) dump(outstream, payload, payload_len); fprintf(outstream, "\n"); return 0; } static void * map_file(const char *filepath, int readonly, uint64_t *filelen) { int fd; off_t len; void *data; fd = open(filepath, readonly ? O_RDONLY : O_RDWR); if (fd < 0) { fprintf(stderr, "Failed to open file %s: %s\n", filepath, strerror(errno)); return NULL; } len = lseek(fd, 0, SEEK_END); if (len < 0 || lseek(fd, 0, SEEK_SET)) { fprintf(stderr, "Failed to calculate file length of %s: %s\n", filepath, strerror(errno)); close(fd); return NULL; } data = mmap(NULL, len, readonly ? PROT_READ : (PROT_READ | PROT_WRITE), readonly ? MAP_PRIVATE : 0, fd, 0); close(fd); if (data == MAP_FAILED) { fprintf(stderr, "Failed to MMAP file %s: %s\n", filepath, strerror(errno)); return NULL; } madvise(data, len, MADV_SEQUENTIAL); *filelen = len; return data; } static void unmap_file(void *mapping, uint64_t len) { munmap(mapping, len); } static int64_t find_section(void *start, uint64_t count, int want_index, const char *want_name) { uint64_t offset = 0; uint8_t *data = start; struct header *hdr; char sectname[sizeof(hdr->name) + 1] = { 0, }; uint32_t payload_len; while (1) { /* Find header start */ if (count < sizeof(struct header)) break; if (!is_header(data + offset, count)) { count--; offset++; continue; } hdr = (struct header *)(data + offset); payload_len = le32_to_cpu(hdr->length); if (count - sizeof(struct header) < payload_len) { fprintf(stderr, "Premature EOF\n"); return -1; } memcpy(sectname, hdr->name, sizeof(hdr->name)); if (want_index >= 0 && want_index != hdr->index) goto next; if (want_name && strcmp(sectname, want_name) != 0) goto next; /* Found it */ return offset; next: count -= sizeof(struct header) + payload_len; offset += sizeof(struct header) + payload_len; } return -1; } static int dump_image(const char *filepath, int want_section_index, const char *want_section_name, int want_headers_only, FILE *outstream) { int err, ret = 0; uint64_t filelen; uint64_t count, offset; int64_t find_offset; uint8_t *data, *section; struct header *hdr; uint32_t payload_len; data = map_file(filepath, 1, &filelen); if (!data) return -EIO; count = filelen; offset = 0; while (1) { find_offset = find_section(data + offset, count, want_section_index, want_section_name); if (find_offset < 0) break; offset += find_offset; count -= find_offset; section = data + offset; hdr = (struct header *)section; payload_len = le32_to_cpu(hdr->length); err = dump_section(hdr, section + sizeof(struct header), payload_len, !want_headers_only, outstream); if (err) { ret = err; goto out; } count -= sizeof(struct header) + payload_len; offset += sizeof(struct header) + payload_len; } out: unmap_file(data, filelen); return ret; } static int write_payload(const char *filepath, int want_section_index, const char *want_section_name, FILE *outstream) { int64_t find_offset; uint64_t filelen; uint8_t *data; struct header *hdr; data = map_file(filepath, 1, &filelen); if (!data) return -EIO; find_offset = find_section(data, filelen, want_section_index, want_section_name); if (find_offset < 0) { fprintf(stderr, "Section %s, index %d not found\n", want_section_name, want_section_index); unmap_file(data, filelen); return -ESRCH; } hdr = (struct header *)(data + find_offset); if (fwrite(data + find_offset + sizeof(struct header), le32_to_cpu(hdr->length), 1, outstream) != 1) { fprintf(stderr, "Could not write output data\n"); unmap_file(data, filelen); return -EIO; } unmap_file(data, filelen); return 0; } static void usage(FILE *fd) { fprintf(fd, "Calvaria - Maemo CAL partition variable access tool\n"); fprintf(fd, "\n"); fprintf(fd, "Usage: calvaria [OPTIONS] FILE\n"); fprintf(fd, "\n"); fprintf(fd, "Actions:\n"); fprintf(fd, " -d|--dump Dump the contents of the image\n"); fprintf(fd, " -H|--headers Dump the headers of the image, only\n"); fprintf(fd, " -p|--payload Write the binary payload to stdout.\n"); fprintf(fd, " Requires -i and -n to be set, too\n"); fprintf(fd, "\n"); fprintf(fd, "Options:\n"); fprintf(fd, " -i|--index NUMBER Use this section index number\n"); fprintf(fd, " -n|--name STRING Use this section name\n"); fprintf(fd, "\n"); fprintf(fd, " -h|--help Print this help text\n"); } enum action { ACTION_NONE, ACTION_DUMP, ACTION_DUMPHDRS, ACTION_GETPAYLOAD, }; int main(int argc, char **argv) { int err, c, idx = 0; const char *filepath; enum action action = ACTION_NONE; int opt_index = -1; const char *opt_name = NULL; static struct option long_options[] = { { "dump", no_argument, 0, 'd', }, { "headers", no_argument, 0, 'H', }, { "payload", no_argument, 0, 'p', }, { "index", required_argument, 0, 'i', }, { "name", required_argument, 0, 'n', }, { "help", no_argument, 0, 'h', }, { }, }; while (1) { c = getopt_long(argc, argv, "dHphi:n:", long_options, &idx); if (c == -1) break; switch (c) { case 'h': usage(stdout); return 0; case 'd': action = ACTION_DUMP; break; case 'H': action = ACTION_DUMPHDRS; break; case 'p': action = ACTION_GETPAYLOAD; break; case 'i': if (sscanf(optarg, "%d", &opt_index) != 1 || opt_index < 0) { fprintf(stderr, "-i|--index is not a positive integer\n"); return 1; } break; case 'n': opt_name = optarg; break; default: return 1; } } argc -= optind; argv += optind; if (action == ACTION_NONE) { fprintf(stderr, "No action specified.\n"); return 1; } if (action == ACTION_GETPAYLOAD) { if (opt_index < 0 || !opt_name) { fprintf(stderr, "Required options -i|--index or -n|--name " "not specified for action -p|--payload\n"); return 1; } } if (argc != 1) { usage(stderr); return 1; } filepath = argv[0]; switch (action) { case ACTION_NONE: break; case ACTION_DUMP: case ACTION_DUMPHDRS: err = dump_image(filepath, opt_index, opt_name, (action == ACTION_DUMPHDRS), stdout); if (err) return 1; break; case ACTION_GETPAYLOAD: err = write_payload(filepath, opt_index, opt_name, stdout); if (err) return 1; break; } return 0; }