Add calvaria. Tool to access Maemo CAL partition.
[openwrt/svn-archive/archive.git] / utils / calvaria / files / src / calvaria.c
1 /*
2 * Calvaria - Maemo CAL partition variable access tool.
3 *
4 * Copyright (c) 2011 Michael Buesch <mb@bu3sch.de>
5 *
6 * Licensed under the GNU General Public License
7 * version 2 or (at your option) any later version.
8 */
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <getopt.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <stdint.h>
18 #include <sys/mman.h>
19
20
21 #define _packed __attribute__((__packed__))
22
23 typedef uint16_t le16_t;
24 typedef uint32_t le32_t;
25
26 struct header {
27 char magic[4]; /* Magic sequence */
28 uint8_t type; /* Type number */
29 uint8_t index; /* Index number */
30 le16_t flags; /* Flags */
31 char name[16]; /* Human readable section name */
32 le32_t length; /* Payload length */
33 le32_t datasum; /* Data CRC32 checksum */
34 le32_t hdrsum; /* Header CRC32 checksum */
35 } _packed;
36
37 #define HDR_MAGIC "ConF"
38
39
40 static char toAscii(char c)
41 {
42 if (c >= 32 && c <= 126)
43 return c;
44 return '.';
45 }
46
47 static void dump(FILE *outstream, const char *buf, size_t size)
48 {
49 size_t i, ascii_cnt = 0;
50 char ascii[17] = { 0, };
51
52 for (i = 0; i < size; i++) {
53 if (i % 16 == 0) {
54 if (i != 0) {
55 fprintf(outstream, " |%s|\n", ascii);
56 ascii[0] = 0;
57 ascii_cnt = 0;
58 }
59 fprintf(outstream, "[%04X]: ", (unsigned int)i);
60 }
61 fprintf(outstream, " %02X", buf[i]);
62 ascii[ascii_cnt] = toAscii(buf[i]);
63 ascii[ascii_cnt + 1] = 0;
64 ascii_cnt++;
65 }
66 if (ascii[0]) {
67 if (size % 16) {
68 for (i = 0; i < 16 - (size % 16); i++)
69 fprintf(outstream, " ");
70 }
71 fprintf(outstream, " |%s|\n", ascii);
72 }
73 fprintf(outstream, "\n");
74 }
75
76 static uint32_t crc32(uint32_t crc, const void *_data, size_t size)
77 {
78 const uint8_t *data = _data;
79 uint8_t value;
80 unsigned int bit;
81 size_t i;
82 const uint32_t poly = 0xEDB88320;
83
84 for (i = 0; i < size; i++) {
85 value = data[i];
86 for (bit = 8; bit; bit--) {
87 if ((crc & 1) != (value & 1))
88 crc = (crc >> 1) ^ poly;
89 else
90 crc >>= 1;
91 value >>= 1;
92 }
93 }
94
95 return crc;
96 }
97
98 static inline uint16_t le16_to_cpu(le16_t x)
99 {
100 uint8_t *bytes = (uint8_t *)&x;
101 uint16_t ret;
102
103 ret = bytes[0];
104 ret |= (uint16_t)(bytes[1]) << 8;
105
106 return ret;
107 }
108
109 static inline uint32_t le32_to_cpu(le32_t x)
110 {
111 uint8_t *bytes = (uint8_t *)&x;
112 uint32_t ret;
113
114 ret = bytes[0];
115 ret |= (uint32_t)(bytes[1]) << 8;
116 ret |= (uint32_t)(bytes[2]) << 16;
117 ret |= (uint32_t)(bytes[3]) << 24;
118
119 return ret;
120 }
121
122 static int is_header(void *data, size_t size)
123 {
124 struct header *hdr = data;
125
126 if (size < sizeof(struct header))
127 return 0;
128 if (memcmp(hdr->magic, HDR_MAGIC, sizeof(hdr->magic)) != 0)
129 return 0;
130 return 1;
131 }
132
133 static int dump_section(const struct header *hdr,
134 const void *payload, size_t payload_len,
135 int dump_payload,
136 FILE *outstream)
137 {
138 char name[sizeof(hdr->name) + 1] = { 0, };
139 int hdrsum_ok, datasum_ok;
140
141 memcpy(name, hdr->name, sizeof(hdr->name));
142 hdrsum_ok = (crc32(0, hdr, sizeof(*hdr) - 4) == le32_to_cpu(hdr->hdrsum));
143 datasum_ok = (crc32(0, payload, payload_len) == le32_to_cpu(hdr->datasum));
144
145 fprintf(outstream, "Section: %s\n", name);
146 fprintf(outstream, "Type: %u (0x%X)\n", hdr->type, hdr->type);
147 fprintf(outstream, "Index: %u (0x%X)\n", hdr->index, hdr->index);
148 fprintf(outstream, "Flags: 0x%04X\n", le16_to_cpu(hdr->flags));
149 fprintf(outstream, "Length: %u (0x%X)\n",
150 le32_to_cpu(hdr->length), le32_to_cpu(hdr->length));
151 fprintf(outstream, "Data CRC32: 0x%08X (%s)\n", le32_to_cpu(hdr->datasum),
152 datasum_ok ? "Ok" : "MISMATCH");
153 fprintf(outstream, "Header CRC32: 0x%08X (%s)\n", le32_to_cpu(hdr->hdrsum),
154 hdrsum_ok ? "Ok" : "MISMATCH");
155 if (dump_payload)
156 dump(outstream, payload, payload_len);
157 fprintf(outstream, "\n");
158
159 return 0;
160 }
161
162 static void * map_file(const char *filepath, int readonly,
163 uint64_t *filelen)
164 {
165 int fd;
166 off_t len;
167 void *data;
168
169 fd = open(filepath, readonly ? O_RDONLY : O_RDWR);
170 if (fd < 0) {
171 fprintf(stderr, "Failed to open file %s: %s\n",
172 filepath, strerror(errno));
173 return NULL;
174 }
175 len = lseek(fd, 0, SEEK_END);
176 if (len < 0 || lseek(fd, 0, SEEK_SET)) {
177 fprintf(stderr, "Failed to calculate file length of %s: %s\n",
178 filepath, strerror(errno));
179 close(fd);
180 return NULL;
181 }
182
183 data = mmap(NULL, len,
184 readonly ? PROT_READ : (PROT_READ | PROT_WRITE),
185 readonly ? MAP_PRIVATE : 0,
186 fd, 0);
187 close(fd);
188 if (data == MAP_FAILED) {
189 fprintf(stderr, "Failed to MMAP file %s: %s\n",
190 filepath, strerror(errno));
191 return NULL;
192 }
193 madvise(data, len, MADV_SEQUENTIAL);
194
195 *filelen = len;
196
197 return data;
198 }
199
200 static void unmap_file(void *mapping, uint64_t len)
201 {
202 munmap(mapping, len);
203 }
204
205 static int64_t find_section(void *start, uint64_t count,
206 int want_index, const char *want_name)
207 {
208 uint64_t offset = 0;
209 uint8_t *data = start;
210 struct header *hdr;
211 char sectname[sizeof(hdr->name) + 1] = { 0, };
212 uint32_t payload_len;
213
214 while (1) {
215 /* Find header start */
216 if (count < sizeof(struct header))
217 break;
218 if (!is_header(data + offset, count)) {
219 count--;
220 offset++;
221 continue;
222 }
223 hdr = (struct header *)(data + offset);
224 payload_len = le32_to_cpu(hdr->length);
225 if (count - sizeof(struct header) < payload_len) {
226 fprintf(stderr, "Premature EOF\n");
227 return -1;
228 }
229 memcpy(sectname, hdr->name, sizeof(hdr->name));
230
231 if (want_index >= 0 && want_index != hdr->index)
232 goto next;
233 if (want_name && strcmp(sectname, want_name) != 0)
234 goto next;
235
236 /* Found it */
237 return offset;
238
239 next:
240 count -= sizeof(struct header) + payload_len;
241 offset += sizeof(struct header) + payload_len;
242 }
243
244 return -1;
245 }
246
247 static int dump_image(const char *filepath,
248 int want_section_index, const char *want_section_name,
249 int want_headers_only,
250 FILE *outstream)
251 {
252 int err, ret = 0;
253 uint64_t filelen;
254 uint64_t count, offset;
255 int64_t find_offset;
256 uint8_t *data, *section;
257 struct header *hdr;
258 uint32_t payload_len;
259
260 data = map_file(filepath, 1, &filelen);
261 if (!data)
262 return -EIO;
263
264 count = filelen;
265 offset = 0;
266 while (1) {
267 find_offset = find_section(data + offset, count,
268 want_section_index, want_section_name);
269 if (find_offset < 0)
270 break;
271 offset += find_offset;
272 count -= find_offset;
273
274 section = data + offset;
275 hdr = (struct header *)section;
276 payload_len = le32_to_cpu(hdr->length);
277
278 err = dump_section(hdr, section + sizeof(struct header),
279 payload_len,
280 !want_headers_only,
281 outstream);
282 if (err) {
283 ret = err;
284 goto out;
285 }
286
287 count -= sizeof(struct header) + payload_len;
288 offset += sizeof(struct header) + payload_len;
289 }
290 out:
291 unmap_file(data, filelen);
292
293 return ret;
294 }
295
296 static int write_payload(const char *filepath,
297 int want_section_index, const char *want_section_name,
298 FILE *outstream)
299 {
300 int64_t find_offset;
301 uint64_t filelen;
302 uint8_t *data;
303 struct header *hdr;
304
305 data = map_file(filepath, 1, &filelen);
306 if (!data)
307 return -EIO;
308
309 find_offset = find_section(data, filelen,
310 want_section_index, want_section_name);
311 if (find_offset < 0) {
312 fprintf(stderr, "Section %s, index %d not found\n",
313 want_section_name, want_section_index);
314 unmap_file(data, filelen);
315 return -ESRCH;
316 }
317
318 hdr = (struct header *)(data + find_offset);
319 if (fwrite(data + find_offset + sizeof(struct header),
320 le32_to_cpu(hdr->length), 1, outstream) != 1) {
321 fprintf(stderr, "Could not write output data\n");
322 unmap_file(data, filelen);
323 return -EIO;
324 }
325
326 unmap_file(data, filelen);
327
328 return 0;
329 }
330
331 static void usage(FILE *fd)
332 {
333 fprintf(fd, "Calvaria - Maemo CAL partition variable access tool\n");
334 fprintf(fd, "\n");
335 fprintf(fd, "Usage: calvaria [OPTIONS] FILE\n");
336 fprintf(fd, "\n");
337 fprintf(fd, "Actions:\n");
338 fprintf(fd, " -d|--dump Dump the contents of the image\n");
339 fprintf(fd, " -H|--headers Dump the headers of the image, only\n");
340 fprintf(fd, " -p|--payload Write the binary payload to stdout.\n");
341 fprintf(fd, " Requires -i and -n to be set, too\n");
342 fprintf(fd, "\n");
343 fprintf(fd, "Options:\n");
344 fprintf(fd, " -i|--index NUMBER Use this section index number\n");
345 fprintf(fd, " -n|--name STRING Use this section name\n");
346 fprintf(fd, "\n");
347 fprintf(fd, " -h|--help Print this help text\n");
348 }
349
350 enum action {
351 ACTION_NONE,
352 ACTION_DUMP,
353 ACTION_DUMPHDRS,
354 ACTION_GETPAYLOAD,
355 };
356
357 int main(int argc, char **argv)
358 {
359 int err, c, idx = 0;
360 const char *filepath;
361 enum action action = ACTION_NONE;
362 int opt_index = -1;
363 const char *opt_name = NULL;
364
365 static struct option long_options[] = {
366 { "dump", no_argument, 0, 'd', },
367 { "headers", no_argument, 0, 'H', },
368 { "payload", no_argument, 0, 'p', },
369 { "index", required_argument, 0, 'i', },
370 { "name", required_argument, 0, 'n', },
371 { "help", no_argument, 0, 'h', },
372 { },
373 };
374
375 while (1) {
376 c = getopt_long(argc, argv, "dHphi:n:",
377 long_options, &idx);
378 if (c == -1)
379 break;
380 switch (c) {
381 case 'h':
382 usage(stdout);
383 return 0;
384 case 'd':
385 action = ACTION_DUMP;
386 break;
387 case 'H':
388 action = ACTION_DUMPHDRS;
389 break;
390 case 'p':
391 action = ACTION_GETPAYLOAD;
392 break;
393 case 'i':
394 if (sscanf(optarg, "%d", &opt_index) != 1 || opt_index < 0) {
395 fprintf(stderr, "-i|--index is not a positive integer\n");
396 return 1;
397 }
398 break;
399 case 'n':
400 opt_name = optarg;
401 break;
402 default:
403 return 1;
404 }
405 }
406 argc -= optind;
407 argv += optind;
408 if (action == ACTION_NONE) {
409 fprintf(stderr, "No action specified.\n");
410 return 1;
411 }
412 if (action == ACTION_GETPAYLOAD) {
413 if (opt_index < 0 || !opt_name) {
414 fprintf(stderr, "Required options -i|--index or -n|--name "
415 "not specified for action -p|--payload\n");
416 return 1;
417 }
418 }
419 if (argc != 1) {
420 usage(stderr);
421 return 1;
422 }
423 filepath = argv[0];
424
425 switch (action) {
426 case ACTION_NONE:
427 break;
428 case ACTION_DUMP:
429 case ACTION_DUMPHDRS:
430 err = dump_image(filepath, opt_index, opt_name,
431 (action == ACTION_DUMPHDRS),
432 stdout);
433 if (err)
434 return 1;
435 break;
436 case ACTION_GETPAYLOAD:
437 err = write_payload(filepath, opt_index, opt_name, stdout);
438 if (err)
439 return 1;
440 break;
441 }
442
443 return 0;
444 }