rpcd: iwinfo plugin fixes
[openwrt.git] / tools / firmware-utils / src / oseama.c
1 /*
2  * oseama
3  *
4  * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  */
11
12 #include <byteswap.h>
13 #include <endian.h>
14 #include <errno.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include "md5.h"
22
23 #if !defined(__BYTE_ORDER)
24 #error "Unknown byte order"
25 #endif
26
27 #if __BYTE_ORDER == __BIG_ENDIAN
28 #define cpu_to_be32(x)  (x)
29 #define be32_to_cpu(x)  (x)
30 #define cpu_to_be16(x)  (x)
31 #define be16_to_cpu(x)  (x)
32 #elif __BYTE_ORDER == __LITTLE_ENDIAN
33 #define cpu_to_be32(x)  bswap_32(x)
34 #define be32_to_cpu(x)  bswap_32(x)
35 #define cpu_to_be16(x)  bswap_16(x)
36 #define be16_to_cpu(x)  bswap_16(x)
37 #else
38 #error "Unsupported endianness"
39 #endif
40
41 #define SEAMA_MAGIC                     0x5ea3a417
42
43 struct seama_seal_header {
44         uint32_t magic;
45         uint16_t reserved;
46         uint16_t metasize;
47         uint32_t imagesize;
48 } __attribute__ ((packed));
49
50 struct seama_entity_header {
51         uint32_t magic;
52         uint16_t reserved;
53         uint16_t metasize;
54         uint32_t imagesize;
55         uint8_t md5[16];
56 } __attribute__ ((packed));
57
58 char *seama_path;
59 int entity_idx = -1;
60
61 static inline size_t oseama_min(size_t x, size_t y) {
62         return x < y ? x : y;
63 }
64
65 /**************************************************
66  * Info
67  **************************************************/
68
69 static void oseama_info_parse_options(int argc, char **argv) {
70         int c;
71
72         while ((c = getopt(argc, argv, "e:")) != -1) {
73                 switch (c) {
74                 case 'e':
75                         entity_idx = atoi(optarg);
76                         break;
77                 }
78         }
79 }
80
81 static int oseama_info_entities(FILE *seama) {
82         struct seama_entity_header hdr;
83         size_t bytes, metasize, imagesize;
84         uint8_t buf[1024];
85         char *end, *tmp;
86         int i = 0;
87         int err = 0;
88
89         while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) {
90                 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
91                         fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
92                         err =  -EINVAL;
93                         goto err_out;
94                 }
95                 metasize = be16_to_cpu(hdr.metasize);
96                 imagesize = be32_to_cpu(hdr.imagesize);
97
98                 if (entity_idx >= 0 && i != entity_idx) {
99                         fseek(seama, metasize + imagesize, SEEK_CUR);
100                         i++;
101                         continue;
102                 }
103
104                 if (metasize >= sizeof(buf)) {
105                         fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize);
106                         err =  -EINVAL;
107                         goto err_out;
108                 }
109
110                 if (entity_idx < 0)
111                         printf("\n");
112                 printf("Entity offset:\t%ld\n", ftell(seama) - sizeof(hdr));
113                 printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize);
114                 printf("Meta size:\t%zd\n", metasize);
115                 printf("Image size:\t%zd\n", imagesize);
116
117                 bytes = fread(buf, 1, metasize, seama);
118                 if (bytes != metasize) {
119                         fprintf(stderr, "Couldn't read %zd B of meta\n", metasize);
120                         err =  -EIO;
121                         goto err_out;
122                 }
123
124                 end = (char *)&buf[metasize - 1];
125                 *end = '\0';
126                 for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
127                         printf("Meta entry:\t%s\n", tmp);
128                 }
129
130                 fseek(seama, imagesize, SEEK_CUR);
131                 i++;
132         }
133
134 err_out:
135         return err;
136 }
137
138 static int oseama_info(int argc, char **argv) {
139         FILE *seama;
140         struct seama_seal_header hdr;
141         size_t bytes;
142         uint16_t metasize;
143         uint32_t imagesize;
144         uint8_t buf[1024];
145         int err = 0;
146
147         if (argc < 3) {
148                 fprintf(stderr, "No Seama file passed\n");
149                 err = -EINVAL;
150                 goto out;
151         }
152         seama_path = argv[2];
153
154         optind = 3;
155         oseama_info_parse_options(argc, argv);
156
157         seama = fopen(seama_path, "r");
158         if (!seama) {
159                 fprintf(stderr, "Couldn't open %s\n", seama_path);
160                 err = -EACCES;
161                 goto out;
162         }
163
164         bytes = fread(&hdr, 1, sizeof(hdr), seama);
165         if (bytes != sizeof(hdr)) {
166                 fprintf(stderr, "Couldn't read %s header\n", seama_path);
167                 err =  -EIO;
168                 goto err_close;
169         }
170         metasize = be16_to_cpu(hdr.metasize);
171         imagesize = be32_to_cpu(hdr.imagesize);
172
173         if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
174                 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
175                 err =  -EINVAL;
176                 goto err_close;
177         }
178
179         if (metasize >= sizeof(buf)) {
180                 fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize);
181                 err =  -EINVAL;
182                 goto err_close;
183         }
184
185         if (imagesize) {
186                 fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize);
187                 err =  -EINVAL;
188                 goto err_close;
189         }
190
191         bytes = fread(buf, 1, metasize, seama);
192         if (bytes != metasize) {
193                 fprintf(stderr, "Couldn't read %d B of meta\n", metasize);
194                 err =  -EIO;
195                 goto err_close;
196         }
197
198         if (entity_idx < 0) {
199                 char *end, *tmp;
200
201                 printf("Meta size:\t%d\n", metasize);
202                 printf("Image size:\t%d\n", imagesize);
203
204                 end = (char *)&buf[metasize - 1];
205                 *end = '\0';
206                 for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
207                         printf("Meta entry:\t%s\n", tmp);
208                 }
209         }
210
211         oseama_info_entities(seama);
212
213 err_close:
214         fclose(seama);
215 out:
216         return err;
217 }
218
219 /**************************************************
220  * Create
221  **************************************************/
222
223 static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) {
224         FILE *in;
225         size_t bytes;
226         ssize_t length = 0;
227         uint8_t buf[128];
228
229         in = fopen(in_path, "r");
230         if (!in) {
231                 fprintf(stderr, "Couldn't open %s\n", in_path);
232                 return -EACCES;
233         }
234
235         while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
236                 if (fwrite(buf, 1, bytes, seama) != bytes) {
237                         fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path);
238                         length = -EIO;
239                         break;
240                 }
241                 length += bytes;
242         }
243
244         fclose(in);
245
246         return length;
247 }
248
249 static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) {
250         uint8_t *buf;
251
252         buf = malloc(length);
253         if (!buf)
254                 return -ENOMEM;
255         memset(buf, 0, length);
256
257         if (fwrite(buf, 1, length, seama) != length) {
258                 fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path);
259                 return -EIO;
260         }
261
262         return length;
263 }
264
265 static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) {
266         if (curr_offset & (alignment - 1)) {
267                 size_t length = alignment - (curr_offset % alignment);
268
269                 return oseama_entity_append_zeros(seama, length);
270         }
271
272         return 0;
273 }
274
275 static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) {
276         struct seama_entity_header hdr = {};
277         uint8_t buf[128];
278         size_t length = imagesize;
279         size_t bytes;
280         MD5_CTX ctx;
281
282         fseek(seama, sizeof(hdr) + metasize, SEEK_SET);
283         MD5_Init(&ctx);
284         while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) {
285                 MD5_Update(&ctx, buf, bytes);
286                 length -= bytes;
287         }
288         MD5_Final(hdr.md5, &ctx);
289
290         hdr.magic = cpu_to_be32(SEAMA_MAGIC);
291         hdr.metasize = cpu_to_be16(metasize);
292         hdr.imagesize = cpu_to_be32(imagesize);
293
294         fseek(seama, 0, SEEK_SET);
295         bytes = fwrite(&hdr, 1, sizeof(hdr), seama);
296         if (bytes != sizeof(hdr)) {
297                 fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path);
298                 return -EIO;
299         }
300
301         return 0;
302 }
303
304 static int oseama_entity(int argc, char **argv) {
305         FILE *seama;
306         ssize_t sbytes;
307         size_t curr_offset = sizeof(struct seama_entity_header);
308         size_t metasize = 0, imagesize = 0;
309         int c;
310         int err = 0;
311
312         if (argc < 3) {
313                 fprintf(stderr, "No Seama file passed\n");
314                 err = -EINVAL;
315                 goto out;
316         }
317         seama_path = argv[2];
318
319         seama = fopen(seama_path, "w+");
320         if (!seama) {
321                 fprintf(stderr, "Couldn't open %s\n", seama_path);
322                 err = -EACCES;
323                 goto out;
324         }
325         fseek(seama, curr_offset, SEEK_SET);
326
327         optind = 3;
328         while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
329                 switch (c) {
330                 case 'm':
331                         sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama);
332                         if (sbytes < 0) {
333                                 fprintf(stderr, "Failed to write meta %s\n", optarg);
334                         } else {
335                                 curr_offset += sbytes;
336                                 metasize += sbytes;
337                         }
338
339                         sbytes = oseama_entity_align(seama, curr_offset, 4);
340                         if (sbytes < 0) {
341                                 fprintf(stderr, "Failed to append zeros\n");
342                         } else {
343                                 curr_offset += sbytes;
344                                 metasize += sbytes;
345                         }
346
347                         break;
348                 case 'f':
349                 case 'b':
350                         break;
351                 }
352         }
353
354         optind = 3;
355         while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
356                 switch (c) {
357                 case 'm':
358                         break;
359                 case 'f':
360                         sbytes = oseama_entity_append_file(seama, optarg);
361                         if (sbytes < 0) {
362                                 fprintf(stderr, "Failed to append file %s\n", optarg);
363                         } else {
364                                 curr_offset += sbytes;
365                                 imagesize += sbytes;
366                         }
367                         break;
368                 case 'b':
369                         sbytes = strtol(optarg, NULL, 0) - curr_offset;
370                         if (sbytes < 0) {
371                                 fprintf(stderr, "Current Seama entity length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
372                         } else {
373                                 sbytes = oseama_entity_append_zeros(seama, sbytes);
374                                 if (sbytes < 0) {
375                                         fprintf(stderr, "Failed to append zeros\n");
376                                 } else {
377                                         curr_offset += sbytes;
378                                         imagesize += sbytes;
379                                 }
380                         }
381                         break;
382                 }
383                 if (err)
384                         break;
385         }
386
387         oseama_entity_write_hdr(seama, metasize, imagesize);
388
389         fclose(seama);
390 out:
391         return err;
392 }
393
394 /**************************************************
395  * Start
396  **************************************************/
397
398 static void usage() {
399         printf("Usage:\n");
400         printf("\n");
401         printf("Info about Seama seal (container):\n");
402         printf("\toseama info <file> [options]\n");
403         printf("\t-e\t\t\t\tprint info about specified entity only\n");
404         printf("\n");
405         printf("Create Seama entity:\n");
406         printf("\toseama entity <file> [options]\n");
407         printf("\t-m meta\t\t\t\tmeta into to put in header\n");
408         printf("\t-f file\t\t\t\tappend content from file\n");
409         printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n");
410 }
411
412 int main(int argc, char **argv) {
413         if (argc > 1) {
414                 if (!strcmp(argv[1], "info"))
415                         return oseama_info(argc, argv);
416                 else if (!strcmp(argv[1], "entity"))
417                         return oseama_entity(argc, argv);
418         }
419
420         usage();
421         return 0;
422 }