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