Add calvaria. Tool to access Maemo CAL partition.
authorMichael Büsch <mb@bu3sch.de>
Sat, 29 Jan 2011 12:38:51 +0000 (12:38 +0000)
committerMichael Büsch <mb@bu3sch.de>
Sat, 29 Jan 2011 12:38:51 +0000 (12:38 +0000)
SVN-Revision: 25235

utils/calvaria/Makefile [new file with mode: 0644]
utils/calvaria/files/src/Makefile [new file with mode: 0644]
utils/calvaria/files/src/calvaria.c [new file with mode: 0644]

diff --git a/utils/calvaria/Makefile b/utils/calvaria/Makefile
new file mode 100644 (file)
index 0000000..01e69a2
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2011 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=calvaria
+PKG_RELEASE:=1
+
+PKG_INSTALL:=1
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/calvaria
+  SECTION:=utils
+  CATEGORY:=Utilities
+  TITLE:=Maemo CAL partition access tool
+  MAINTAINER:=Michael Buesch <mb@bu3sch.de>
+endef
+
+define Package/calvaria/description
+  Calvaria - Maemo CAL partition variable access tool
+  The CAL partition (/dev/mtdblock1) is used to store configuration
+  and calibration data.
+endef
+
+define Build/Prepare
+       $(CP) ./files/src/* $(PKG_BUILD_DIR)/
+endef
+
+define Package/calvaria/install
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/calvaria $(1)/usr/bin/
+endef
+
+$(eval $(call BuildPackage,calvaria))
diff --git a/utils/calvaria/files/src/Makefile b/utils/calvaria/files/src/Makefile
new file mode 100644 (file)
index 0000000..f977e9b
--- /dev/null
@@ -0,0 +1,26 @@
+DESTDIR                ?=
+PREFIX         ?= /usr
+CFLAGS         ?= -O2 -Wall
+LDFLAGS                ?=
+LDLIBS         ?=
+
+STRIP          ?= strip
+INSTALL                ?= install
+
+BIN            := calvaria
+
+all: $(BIN)
+
+$(BIN): calvaria.o
+       $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
+       $(STRIP) $@
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $^ -o $@
+
+install: $(BIN)
+       $(INSTALL) -d $(DESTDIR)/$(PREFIX)/bin
+       $(INSTALL) -m 0755 $(BIN) $(DESTDIR)/$(PREFIX)/bin/
+
+clean:
+       rm -f $(BIN) *.o
diff --git a/utils/calvaria/files/src/calvaria.c b/utils/calvaria/files/src/calvaria.c
new file mode 100644 (file)
index 0000000..3f61dca
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Calvaria - Maemo CAL partition variable access tool.
+ *
+ * Copyright (c) 2011 Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU General Public License
+ * version 2 or (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+
+#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;
+}