firmware-utils: bcm4908img: replace size with offset
[project/firmware-utils.git] / src / bcm4908img.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
4 */
5
6 #include <byteswap.h>
7 #include <endian.h>
8 #include <errno.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16
17 #if !defined(__BYTE_ORDER)
18 #error "Unknown byte order"
19 #endif
20
21 #if __BYTE_ORDER == __BIG_ENDIAN
22 #define cpu_to_le32(x) bswap_32(x)
23 #define le32_to_cpu(x) bswap_32(x)
24 #define cpu_to_be32(x) (x)
25 #define be32_to_cpu(x) (x)
26 #define cpu_to_le16(x) bswap_16(x)
27 #define le16_to_cpu(x) bswap_16(x)
28 #define cpu_to_be16(x) (x)
29 #define be16_to_cpu(x) (x)
30 #elif __BYTE_ORDER == __LITTLE_ENDIAN
31 #define cpu_to_le32(x) (x)
32 #define le32_to_cpu(x) (x)
33 #define cpu_to_be32(x) bswap_32(x)
34 #define be32_to_cpu(x) bswap_32(x)
35 #define cpu_to_le16(x) (x)
36 #define le16_to_cpu(x) (x)
37 #define cpu_to_be16(x) bswap_16(x)
38 #define be16_to_cpu(x) bswap_16(x)
39 #else
40 #error "Unsupported endianness"
41 #endif
42
43 #define WFI_VERSION 0x00005732
44 #define WFI_VERSION_NAND_1MB_DATA 0x00005731
45
46 #define WFI_NOR_FLASH 1
47 #define WFI_NAND16_FLASH 2
48 #define WFI_NAND128_FLASH 3
49 #define WFI_NAND256_FLASH 4
50 #define WFI_NAND512_FLASH 5
51 #define WFI_NAND1024_FLASH 6
52 #define WFI_NAND2048_FLASH 7
53
54 #define WFI_FLAG_HAS_PMC 0x1
55 #define WFI_FLAG_SUPPORTS_BTRM 0x2
56
57 static int debug;
58
59 struct bcm4908img_tail {
60 uint32_t crc32;
61 uint32_t version;
62 uint32_t chip_id;
63 uint32_t flash_type;
64 uint32_t flags;
65 };
66
67 /**
68 * struct bcm4908img_info - info about BCM4908 image
69 *
70 * Standard BCM4908 image consists of:
71 * 1. (Optional) vedor header
72 * 2. (Optional) cferom
73 * 3. bootfs ─┐
74 * 4. padding ├─ firmware
75 * 5. rootfs ─┘
76 * 6. BCM4908 tail
77 */
78 struct bcm4908img_info {
79 size_t file_size;
80 size_t cferom_offset;
81 size_t bootfs_offset;
82 uint32_t crc32; /* Calculated checksum */
83 struct bcm4908img_tail tail;
84 };
85
86 char *pathname;
87
88 static inline size_t bcm4908img_min(size_t x, size_t y) {
89 return x < y ? x : y;
90 }
91
92 /**************************************************
93 * CRC32
94 **************************************************/
95
96 static const uint32_t crc32_tbl[] = {
97 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
98 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
99 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
100 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
101 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
102 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
103 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
104 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
105 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
106 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
107 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
108 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
109 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
110 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
111 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
112 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
113 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
114 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
115 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
116 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
117 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
118 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
119 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
120 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
121 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
122 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
123 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
124 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
125 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
126 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
127 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
128 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
129 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
130 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
131 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
132 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
133 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
134 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
135 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
136 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
137 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
138 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
139 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
140 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
141 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
142 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
143 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
144 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
145 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
146 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
147 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
148 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
149 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
150 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
151 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
152 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
153 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
154 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
155 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
156 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
157 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
158 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
159 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
160 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
161 };
162
163 uint32_t bcm4908img_crc32(uint32_t crc, const void *buf, size_t len) {
164 const uint8_t *in = buf;
165
166 while (len) {
167 crc = crc32_tbl[(crc ^ *in) & 0xff] ^ (crc >> 8);
168 in++;
169 len--;
170 }
171
172 return crc;
173 }
174
175 /**************************************************
176 * Helpers
177 **************************************************/
178
179 static FILE *bcm4908img_open(const char *pathname, const char *mode) {
180 struct stat st;
181
182 if (pathname)
183 return fopen(pathname, mode);
184
185 if (isatty(fileno(stdin))) {
186 fprintf(stderr, "Reading from TTY stdin is unsupported\n");
187 return NULL;
188 }
189
190 if (fstat(fileno(stdin), &st)) {
191 fprintf(stderr, "Failed to fstat stdin: %d\n", -errno);
192 return NULL;
193 }
194
195 if (S_ISFIFO(st.st_mode)) {
196 fprintf(stderr, "Reading from pipe stdin is unsupported\n");
197 return NULL;
198 }
199
200 return stdin;
201 }
202
203 static void bcm4908img_close(FILE *fp) {
204 if (fp != stdin)
205 fclose(fp);
206 }
207
208 static int bcm4908img_calc_crc32(FILE *fp, struct bcm4908img_info *info) {
209 uint8_t buf[1024];
210 size_t length;
211 size_t bytes;
212
213 /* Start with cferom (or bootfs) - skip vendor header */
214 fseek(fp, info->cferom_offset, SEEK_SET);
215
216 info->crc32 = 0xffffffff;
217 length = info->file_size - info->cferom_offset - sizeof(struct bcm4908img_tail);
218 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
219 info->crc32 = bcm4908img_crc32(info->crc32, buf, bytes);
220 length -= bytes;
221 }
222 if (length) {
223 fprintf(stderr, "Failed to read last %zd B of data\n", length);
224 return -EIO;
225 }
226
227 return 0;
228 }
229
230 /**************************************************
231 * Existing firmware parser
232 **************************************************/
233
234 struct chk_header {
235 uint32_t magic;
236 uint32_t header_len;
237 uint8_t reserved[8];
238 uint32_t kernel_chksum;
239 uint32_t rootfs_chksum;
240 uint32_t kernel_len;
241 uint32_t rootfs_len;
242 uint32_t image_chksum;
243 uint32_t header_chksum;
244 char board_id[0];
245 };
246
247 static int bcm4908img_parse(FILE *fp, struct bcm4908img_info *info) {
248 struct bcm4908img_tail *tail = &info->tail;
249 struct chk_header *chk;
250 struct stat st;
251 uint8_t buf[1024];
252 uint16_t tmp16;
253 size_t length;
254 size_t bytes;
255 int err = 0;
256
257 memset(info, 0, sizeof(*info));
258
259 /* File size */
260
261 if (fstat(fileno(fp), &st)) {
262 err = -errno;
263 fprintf(stderr, "Failed to fstat: %d\n", err);
264 return err;
265 }
266 info->file_size = st.st_size;
267
268 /* Vendor formats */
269
270 rewind(fp);
271 if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
272 fprintf(stderr, "Failed to read file header\n");
273 return -EIO;
274 }
275 chk = (void *)buf;
276 if (be32_to_cpu(chk->magic) == 0x2a23245e)
277 info->cferom_offset = be32_to_cpu(chk->header_len);
278
279 /* Offsets */
280
281 for (info->bootfs_offset = info->cferom_offset;
282 info->bootfs_offset < info->file_size;
283 info->bootfs_offset += 0x20000) {
284 if (fseek(fp, info->bootfs_offset, SEEK_SET)) {
285 err = -errno;
286 fprintf(stderr, "Failed to fseek to the 0x%zx\n", info->bootfs_offset);
287 return err;
288 }
289 if (fread(&tmp16, 1, sizeof(tmp16), fp) != sizeof(tmp16)) {
290 fprintf(stderr, "Failed to read while looking for JFFS2\n");
291 return -EIO;
292 }
293 if (be16_to_cpu(tmp16) == 0x8519)
294 break;
295 }
296 if (info->bootfs_offset >= info->file_size) {
297 fprintf(stderr, "Failed to find bootfs offset\n");
298 return -EPROTO;
299 }
300
301 /* CRC32 */
302
303 /* Start with cferom (or bootfs) - skip vendor header */
304 fseek(fp, info->cferom_offset, SEEK_SET);
305
306 info->crc32 = 0xffffffff;
307 length = info->file_size - info->cferom_offset - sizeof(*tail);
308 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
309 info->crc32 = bcm4908img_crc32(info->crc32, buf, bytes);
310 length -= bytes;
311 }
312 if (length) {
313 fprintf(stderr, "Failed to read last %zd B of data\n", length);
314 return -EIO;
315 }
316
317 /* Tail */
318
319 if (fread(tail, 1, sizeof(*tail), fp) != sizeof(*tail)) {
320 fprintf(stderr, "Failed to read BCM4908 image tail\n");
321 return -EIO;
322 }
323
324 /* Standard validation */
325
326 if (info->crc32 != le32_to_cpu(tail->crc32)) {
327 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info->crc32, le32_to_cpu(tail->crc32));
328 return -EPROTO;
329 }
330
331 return 0;
332 }
333
334 /**************************************************
335 * Info
336 **************************************************/
337
338 static int bcm4908img_info(int argc, char **argv) {
339 struct bcm4908img_info info;
340 const char *pathname = NULL;
341 FILE *fp;
342 int c;
343 int err = 0;
344
345 while ((c = getopt(argc, argv, "i:")) != -1) {
346 switch (c) {
347 case 'i':
348 pathname = optarg;
349 break;
350 }
351 }
352
353 fp = bcm4908img_open(pathname, "r");
354 if (!fp) {
355 fprintf(stderr, "Failed to open BCM4908 image\n");
356 err = -EACCES;
357 goto out;
358 }
359
360 err = bcm4908img_parse(fp, &info);
361 if (err) {
362 fprintf(stderr, "Failed to parse BCM4908 image\n");
363 goto err_close;
364 }
365
366 if (info.bootfs_offset != info.cferom_offset)
367 printf("cferom offset:\t%zu\n", info.cferom_offset);
368 printf("bootfs offset:\t0x%zx\n", info.bootfs_offset);
369 printf("Checksum:\t0x%08x\n", info.crc32);
370
371 err_close:
372 bcm4908img_close(fp);
373 out:
374 return err;
375 }
376
377 /**************************************************
378 * Create
379 **************************************************/
380
381 static ssize_t bcm4908img_create_append_file(FILE *trx, const char *in_path, uint32_t *crc32) {
382 FILE *in;
383 size_t bytes;
384 ssize_t length = 0;
385 uint8_t buf[1024];
386
387 in = fopen(in_path, "r");
388 if (!in) {
389 fprintf(stderr, "Failed to open %s\n", in_path);
390 return -EACCES;
391 }
392
393 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
394 if (fwrite(buf, 1, bytes, trx) != bytes) {
395 fprintf(stderr, "Failed to write %zu B to %s\n", bytes, pathname);
396 length = -EIO;
397 break;
398 }
399 *crc32 = bcm4908img_crc32(*crc32, buf, bytes);
400 length += bytes;
401 }
402
403 fclose(in);
404
405 return length;
406 }
407
408 static ssize_t bcm4908img_create_append_zeros(FILE *trx, size_t length) {
409 uint8_t *buf;
410
411 buf = malloc(length);
412 if (!buf)
413 return -ENOMEM;
414 memset(buf, 0, length);
415
416 if (fwrite(buf, 1, length, trx) != length) {
417 fprintf(stderr, "Failed to write %zu B to %s\n", length, pathname);
418 free(buf);
419 return -EIO;
420 }
421
422 free(buf);
423
424 return length;
425 }
426
427 static ssize_t bcm4908img_create_align(FILE *trx, size_t cur_offset, size_t alignment) {
428 if (cur_offset & (alignment - 1)) {
429 size_t length = alignment - (cur_offset % alignment);
430 return bcm4908img_create_append_zeros(trx, length);
431 }
432
433 return 0;
434 }
435
436 static int bcm4908img_create(int argc, char **argv) {
437 struct bcm4908img_tail tail = {
438 .version = cpu_to_le32(WFI_VERSION),
439 .chip_id = cpu_to_le32(0x4908),
440 .flash_type = cpu_to_le32(WFI_NAND128_FLASH),
441 .flags = cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM),
442 };
443 uint32_t crc32 = 0xffffffff;
444 size_t cur_offset = 0;
445 ssize_t bytes;
446 FILE *fp;
447 int c;
448 int err = 0;
449
450 if (argc < 3) {
451 fprintf(stderr, "No BCM4908 image pathname passed\n");
452 err = -EINVAL;
453 goto out;
454 }
455 pathname = argv[2];
456
457 fp = fopen(pathname, "w+");
458 if (!fp) {
459 fprintf(stderr, "Failed to open %s\n", pathname);
460 err = -EACCES;
461 goto out;
462 }
463
464 optind = 3;
465 while ((c = getopt(argc, argv, "f:a:A:")) != -1) {
466 switch (c) {
467 case 'f':
468 bytes = bcm4908img_create_append_file(fp, optarg, &crc32);
469 if (bytes < 0) {
470 fprintf(stderr, "Failed to append file %s\n", optarg);
471 } else {
472 cur_offset += bytes;
473 }
474 break;
475 case 'a':
476 bytes = bcm4908img_create_align(fp, cur_offset, strtol(optarg, NULL, 0));
477 if (bytes < 0)
478 fprintf(stderr, "Failed to append zeros\n");
479 else
480 cur_offset += bytes;
481 break;
482 case 'A':
483 bytes = strtol(optarg, NULL, 0) - cur_offset;
484 if (bytes < 0) {
485 fprintf(stderr, "Current BCM4908 image length is 0x%zx, can't pad it with zeros to 0x%lx\n", cur_offset, strtol(optarg, NULL, 0));
486 } else {
487 bytes = bcm4908img_create_append_zeros(fp, bytes);
488 if (bytes < 0)
489 fprintf(stderr, "Failed to append zeros\n");
490 else
491 cur_offset += bytes;
492 }
493 break;
494 }
495 if (err)
496 goto err_close;
497 }
498
499 tail.crc32 = cpu_to_le32(crc32);
500
501 bytes = fwrite(&tail, 1, sizeof(tail), fp);
502 if (bytes != sizeof(tail)) {
503 fprintf(stderr, "Failed to write BCM4908 image tail to %s\n", pathname);
504 return -EIO;
505 }
506
507 err_close:
508 fclose(fp);
509 out:
510 return err;
511 }
512
513 /**************************************************
514 * Extract
515 **************************************************/
516
517 static int bcm4908img_extract(int argc, char **argv) {
518 struct bcm4908img_info info;
519 const char *pathname = NULL;
520 uint8_t buf[1024];
521 const char *type;
522 size_t offset;
523 size_t length;
524 size_t bytes;
525 FILE *fp;
526 int c;
527 int err = 0;
528
529 while ((c = getopt(argc, argv, "i:t:")) != -1) {
530 switch (c) {
531 case 'i':
532 pathname = optarg;
533 break;
534 case 't':
535 type = optarg;
536 break;
537 }
538 }
539
540 fp = bcm4908img_open(pathname, "r");
541 if (!fp) {
542 fprintf(stderr, "Failed to open BCM4908 image\n");
543 err = -EACCES;
544 goto err_out;
545 }
546
547 err = bcm4908img_parse(fp, &info);
548 if (err) {
549 fprintf(stderr, "Failed to parse BCM4908 image\n");
550 goto err_close;
551 }
552
553 if (!strcmp(type, "cferom")) {
554 offset = 0;
555 length = info.bootfs_offset - offset;
556 if (!length) {
557 err = -ENOENT;
558 fprintf(stderr, "This BCM4908 image doesn't contain cferom\n");
559 goto err_close;
560 }
561 } else if (!strcmp(type, "firmware")) {
562 offset = info.bootfs_offset;
563 length = info.file_size - offset - sizeof(struct bcm4908img_tail);
564 } else {
565 err = -EINVAL;
566 fprintf(stderr, "Unsupported extract type: %s\n", type);
567 goto err_close;
568 }
569
570 if (!length) {
571 err = -EINVAL;
572 fprintf(stderr, "No data to extract specified\n");
573 goto err_close;
574 }
575
576 fseek(fp, offset, SEEK_SET);
577 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
578 fwrite(buf, bytes, 1, stdout);
579 length -= bytes;
580 }
581 if (length) {
582 err = -EIO;
583 fprintf(stderr, "Failed to read last %zd B of data\n", length);
584 goto err_close;
585 }
586
587 err_close:
588 bcm4908img_close(fp);
589 err_out:
590 return err;
591 }
592
593 /**************************************************
594 * bootfs
595 **************************************************/
596
597 #define JFFS2_MAGIC_BITMASK 0x1985
598
599 #define JFFS2_COMPR_NONE 0x00
600 #define JFFS2_COMPR_ZERO 0x01
601 #define JFFS2_COMPR_RTIME 0x02
602 #define JFFS2_COMPR_RUBINMIPS 0x03
603 #define JFFS2_COMPR_COPY 0x04
604 #define JFFS2_COMPR_DYNRUBIN 0x05
605 #define JFFS2_COMPR_ZLIB 0x06
606 #define JFFS2_COMPR_LZO 0x07
607 /* Compatibility flags. */
608 #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
609 #define JFFS2_NODE_ACCURATE 0x2000
610 /* INCOMPAT: Fail to mount the filesystem */
611 #define JFFS2_FEATURE_INCOMPAT 0xc000
612 /* ROCOMPAT: Mount read-only */
613 #define JFFS2_FEATURE_ROCOMPAT 0x8000
614 /* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
615 #define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
616 /* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
617 #define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
618
619 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
620
621 typedef struct {
622 uint32_t v32;
623 } __attribute__((packed)) jint32_t;
624
625 typedef struct {
626 uint16_t v16;
627 } __attribute__((packed)) jint16_t;
628
629 struct jffs2_unknown_node
630 {
631 /* All start like this */
632 jint16_t magic;
633 jint16_t nodetype;
634 jint32_t totlen; /* So we can skip over nodes we don't grok */
635 jint32_t hdr_crc;
636 };
637
638 struct jffs2_raw_dirent
639 {
640 jint16_t magic;
641 jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */
642 jint32_t totlen;
643 jint32_t hdr_crc;
644 jint32_t pino;
645 jint32_t version;
646 jint32_t ino; /* == zero for unlink */
647 jint32_t mctime;
648 uint8_t nsize;
649 uint8_t type;
650 uint8_t unused[2];
651 jint32_t node_crc;
652 jint32_t name_crc;
653 uint8_t name[0];
654 };
655
656 #define je16_to_cpu(x) ((x).v16)
657 #define je32_to_cpu(x) ((x).v32)
658
659 static int bcm4908img_bootfs_ls(FILE *fp, struct bcm4908img_info *info) {
660 struct jffs2_unknown_node node;
661 struct jffs2_raw_dirent dirent;
662 size_t offset;
663 size_t bytes;
664 int err = 0;
665
666 for (offset = info->bootfs_offset; ; offset += (je32_to_cpu(node.totlen) + 0x03) & ~0x03) {
667 char name[FILENAME_MAX + 1];
668
669 if (fseek(fp, offset, SEEK_SET)) {
670 err = -errno;
671 fprintf(stderr, "Failed to fseek: %d\n", err);
672 return err;
673 }
674
675 bytes = fread(&node, 1, sizeof(node), fp);
676 if (bytes != sizeof(node)) {
677 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
678 return -EIO;
679 }
680
681 if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK) {
682 break;
683 }
684
685 if (je16_to_cpu(node.nodetype) != JFFS2_NODETYPE_DIRENT) {
686 continue;
687 }
688
689 memcpy(&dirent, &node, sizeof(node));
690 bytes += fread((uint8_t *)&dirent + sizeof(node), 1, sizeof(dirent) - sizeof(node), fp);
691 if (bytes != sizeof(dirent)) {
692 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
693 return -EIO;
694 }
695
696 if (dirent.nsize + 1 > sizeof(name)) {
697 /* Keep reading & printing BUT exit with error code */
698 fprintf(stderr, "Too long filename\n");
699 err = -ENOMEM;
700 continue;
701 }
702
703 bytes = fread(name, 1, dirent.nsize, fp);
704 if (bytes != dirent.nsize) {
705 fprintf(stderr, "Failed to read filename\n");
706 return -EIO;
707 }
708 name[bytes] = '\0';
709
710 printf("%s\n", name);
711 }
712
713 return err;
714 }
715
716 static int bcm4908img_bootfs_mv(FILE *fp, struct bcm4908img_info *info, int argc, char **argv) {
717 struct jffs2_unknown_node node;
718 struct jffs2_raw_dirent dirent;
719 const char *oldname;
720 const char *newname;
721 size_t offset;
722 size_t bytes;
723 int err = -ENOENT;
724
725 if (argc - optind < 2) {
726 fprintf(stderr, "No enough arguments passed\n");
727 return -EINVAL;
728 }
729 oldname = argv[optind++];
730 newname = argv[optind++];
731
732 if (strlen(newname) != strlen(oldname)) {
733 fprintf(stderr, "New filename must have the same length as the old one\n");
734 return -EINVAL;
735 }
736
737 for (offset = info->bootfs_offset; ; offset += (je32_to_cpu(node.totlen) + 0x03) & ~0x03) {
738 char name[FILENAME_MAX];
739 uint32_t crc32;
740
741 if (fseek(fp, offset, SEEK_SET)) {
742 err = -errno;
743 fprintf(stderr, "Failed to fseek: %d\n", err);
744 return err;
745 }
746
747 bytes = fread(&node, 1, sizeof(node), fp);
748 if (bytes != sizeof(node)) {
749 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
750 return -EIO;
751 }
752
753 if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK) {
754 break;
755 }
756
757 if (je16_to_cpu(node.nodetype) != JFFS2_NODETYPE_DIRENT) {
758 continue;
759 }
760
761 bytes += fread((uint8_t *)&dirent + sizeof(node), 1, sizeof(dirent) - sizeof(node), fp);
762 if (bytes != sizeof(dirent)) {
763 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
764 return -EIO;
765 }
766
767 if (dirent.nsize + 1 > sizeof(name)) {
768 fprintf(stderr, "Too long filename\n");
769 err = -ENOMEM;
770 continue;
771 }
772
773 bytes = fread(name, 1, dirent.nsize, fp);
774 if (bytes != dirent.nsize) {
775 fprintf(stderr, "Failed to read filename\n");
776 return -EIO;
777 }
778 name[bytes] = '\0';
779
780 if (debug)
781 printf("offset:%08zx name_crc:%04x filename:%s\n", offset, je32_to_cpu(dirent.name_crc), name);
782
783 if (strcmp(name, oldname)) {
784 continue;
785 }
786
787 if (fseek(fp, offset + offsetof(struct jffs2_raw_dirent, name_crc), SEEK_SET)) {
788 err = -errno;
789 fprintf(stderr, "Failed to fseek: %d\n", err);
790 return err;
791 }
792 crc32 = bcm4908img_crc32(0, newname, dirent.nsize);
793 bytes = fwrite(&crc32, 1, sizeof(crc32), fp);
794 if (bytes != sizeof(crc32)) {
795 fprintf(stderr, "Failed to write new CRC32\n");
796 return -EIO;
797 }
798
799 if (fseek(fp, offset + offsetof(struct jffs2_raw_dirent, name), SEEK_SET)) {
800 err = -errno;
801 fprintf(stderr, "Failed to fseek: %d\n", err);
802 return err;
803 }
804 bytes = fwrite(newname, 1, dirent.nsize, fp);
805 if (bytes != dirent.nsize) {
806 fprintf(stderr, "Failed to write new filename\n");
807 return -EIO;
808 }
809
810 /* Calculate new BCM4908 image checksum */
811
812 err = bcm4908img_calc_crc32(fp, info);
813 if (err) {
814 fprintf(stderr, "Failed to write new filename\n");
815 return err;
816 }
817
818 info->tail.crc32 = cpu_to_le32(info->crc32);
819 if (fseek(fp, -sizeof(struct bcm4908img_tail), SEEK_END)) {
820 err = -errno;
821 fprintf(stderr, "Failed to write new filename\n");
822 return err;
823 }
824
825 if (fwrite(&info->tail, 1, sizeof(struct bcm4908img_tail), fp) != sizeof(struct bcm4908img_tail)) {
826 fprintf(stderr, "Failed to write updated tail\n");
827 return -EIO;
828 }
829
830 printf("Successfully renamed %s to the %s\n", oldname, newname);
831
832 return 0;
833 }
834
835 fprintf(stderr, "Failed to find %s\n", oldname);
836
837 return -ENOENT;
838 }
839
840 static int bcm4908img_bootfs(int argc, char **argv) {
841 struct bcm4908img_info info;
842 const char *pathname = NULL;
843 const char *mode;
844 const char *cmd;
845 FILE *fp;
846 int c;
847 int err = 0;
848
849 while ((c = getopt(argc, argv, "i:")) != -1) {
850 switch (c) {
851 case 'i':
852 pathname = optarg;
853 break;
854 }
855 }
856
857 if (argc - optind < 1) {
858 fprintf(stderr, "No bootfs command specified\n");
859 err = -EINVAL;
860 goto out;
861 }
862 cmd = argv[optind++];
863
864 mode = strcmp(cmd, "mv") ? "r" : "r+";
865 fp = bcm4908img_open(pathname, mode);
866 if (!fp) {
867 fprintf(stderr, "Failed to open BCM4908 image\n");
868 err = -EACCES;
869 goto out;
870 }
871
872 err = bcm4908img_parse(fp, &info);
873 if (err) {
874 fprintf(stderr, "Failed to parse BCM4908 image\n");
875 goto err_close;
876 }
877
878 if (!strcmp(cmd, "ls")) {
879 err = bcm4908img_bootfs_ls(fp, &info);
880 } else if (!strcmp(cmd, "mv")) {
881 err = bcm4908img_bootfs_mv(fp, &info, argc, argv);
882 } else {
883 err = -EINVAL;
884 fprintf(stderr, "Unsupported bootfs command: %s\n", cmd);
885 }
886
887 err_close:
888 bcm4908img_close(fp);
889 out:
890 return err;
891 }
892
893 /**************************************************
894 * Start
895 **************************************************/
896
897 static void usage() {
898 printf("Usage:\n");
899 printf("\n");
900 printf("Info about a BCM4908 image:\n");
901 printf("\tbcm4908img info <options>\n");
902 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
903 printf("\n");
904 printf("Creating a new BCM4908 image:\n");
905 printf("\tbcm4908img create <file> [options]\n");
906 printf("\t-f file\t\t\t\tadd data from specified file\n");
907 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
908 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
909 printf("\n");
910 printf("Extracting from a BCM4908 image:\n");
911 printf("\tbcm4908img extract <options>\n");
912 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
913 printf("\t-t <type>\t\t\t\tone of: cferom, bootfs, rootfs, firmware\n");
914 printf("\n");
915 printf("Access bootfs in a BCM4908 image:\n");
916 printf("\tbcm4908img bootfs <options> <command> <arguments>\n");
917 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
918 printf("\tls\t\t\t\t\tlist bootfs files\n");
919 printf("\tmv <source> <dest>\t\t\trename bootfs file\n");
920 }
921
922 int main(int argc, char **argv) {
923 if (argc > 1) {
924 optind++;
925 if (!strcmp(argv[1], "info"))
926 return bcm4908img_info(argc, argv);
927 else if (!strcmp(argv[1], "create"))
928 return bcm4908img_create(argc, argv);
929 else if (!strcmp(argv[1], "extract"))
930 return bcm4908img_extract(argc, argv);
931 else if (!strcmp(argv[1], "bootfs"))
932 return bcm4908img_bootfs(argc, argv);
933 }
934
935 usage();
936 return 0;
937 }