1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
18 #if !defined(__BYTE_ORDER)
19 #error "Unknown byte order"
22 #if __BYTE_ORDER == __BIG_ENDIAN
23 #define cpu_to_le32(x) bswap_32(x)
24 #define le32_to_cpu(x) bswap_32(x)
25 #define cpu_to_be32(x) (x)
26 #define be32_to_cpu(x) (x)
27 #define cpu_to_le16(x) bswap_16(x)
28 #define le16_to_cpu(x) bswap_16(x)
29 #define cpu_to_be16(x) (x)
30 #define be16_to_cpu(x) (x)
31 #elif __BYTE_ORDER == __LITTLE_ENDIAN
32 #define cpu_to_le32(x) (x)
33 #define le32_to_cpu(x) (x)
34 #define cpu_to_be32(x) bswap_32(x)
35 #define be32_to_cpu(x) bswap_32(x)
36 #define cpu_to_le16(x) (x)
37 #define le16_to_cpu(x) (x)
38 #define cpu_to_be16(x) bswap_16(x)
39 #define be16_to_cpu(x) bswap_16(x)
41 #error "Unsupported endianness"
44 #define WFI_VERSION 0x00005732
45 #define WFI_VERSION_NAND_1MB_DATA 0x00005731
47 #define WFI_NOR_FLASH 1
48 #define WFI_NAND16_FLASH 2
49 #define WFI_NAND128_FLASH 3
50 #define WFI_NAND256_FLASH 4
51 #define WFI_NAND512_FLASH 5
52 #define WFI_NAND1024_FLASH 6
53 #define WFI_NAND2048_FLASH 7
55 #define WFI_FLAG_HAS_PMC 0x1
56 #define WFI_FLAG_SUPPORTS_BTRM 0x2
58 #define UBI_EC_HDR_MAGIC 0x55424923
62 struct bcm4908img_tail
{
71 * struct bcm4908img_info - info about BCM4908 image
73 * Standard BCM4908 image consists of:
74 * 1. (Optional) vedor header
75 * 2. (Optional) cferom
77 * 4. padding ├─ firmware
80 * 7. (Optional) vendor tail
82 struct bcm4908img_info
{
85 size_t padding_offset
;
88 uint32_t crc32
; /* Calculated checksum */
89 struct bcm4908img_tail tail
;
94 static inline size_t bcm4908img_min(size_t x
, size_t y
) {
98 /**************************************************
100 **************************************************/
102 static const uint32_t crc32_tbl
[] = {
103 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
104 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
105 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
106 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
107 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
108 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
109 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
110 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
111 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
112 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
113 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
114 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
115 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
116 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
117 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
118 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
119 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
120 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
121 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
122 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
123 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
124 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
125 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
126 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
127 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
128 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
129 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
130 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
131 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
132 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
133 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
134 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
135 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
136 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
137 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
138 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
139 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
140 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
141 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
142 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
143 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
144 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
145 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
146 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
147 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
148 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
149 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
150 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
151 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
152 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
153 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
154 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
155 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
156 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
157 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
158 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
159 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
160 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
161 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
162 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
163 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
164 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
165 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
166 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
169 uint32_t bcm4908img_crc32(uint32_t crc
, const void *buf
, size_t len
) {
170 const uint8_t *in
= buf
;
173 crc
= crc32_tbl
[(crc
^ *in
) & 0xff] ^ (crc
>> 8);
181 /**************************************************
183 **************************************************/
185 static FILE *bcm4908img_open(const char *pathname
, const char *mode
) {
189 return fopen(pathname
, mode
);
191 if (isatty(fileno(stdin
))) {
192 fprintf(stderr
, "Reading from TTY stdin is unsupported\n");
196 if (fstat(fileno(stdin
), &st
)) {
197 fprintf(stderr
, "Failed to fstat stdin: %d\n", -errno
);
201 if (S_ISFIFO(st
.st_mode
)) {
202 fprintf(stderr
, "Reading from pipe stdin is unsupported\n");
209 static void bcm4908img_close(FILE *fp
) {
214 static int bcm4908img_calc_crc32(FILE *fp
, struct bcm4908img_info
*info
) {
219 /* Start with cferom (or bootfs) - skip vendor header */
220 fseek(fp
, info
->cferom_offset
, SEEK_SET
);
222 info
->crc32
= 0xffffffff;
223 length
= info
->tail_offset
- info
->cferom_offset
;
224 while (length
&& (bytes
= fread(buf
, 1, bcm4908img_min(sizeof(buf
), length
), fp
)) > 0) {
225 info
->crc32
= bcm4908img_crc32(info
->crc32
, buf
, bytes
);
229 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
236 /**************************************************
237 * Existing firmware parser
238 **************************************************/
244 uint32_t kernel_chksum
;
245 uint32_t rootfs_chksum
;
248 uint32_t image_chksum
;
249 uint32_t header_chksum
;
253 struct linksys_tail
{
259 uint8_t signature
[16];
260 uint8_t reserved
[192];
263 static bool bcm4908img_is_all_ff(const void *buf
, size_t length
)
265 const uint8_t *in
= buf
;
268 for (i
= 0; i
< length
; i
++) {
276 static int bcm4908img_parse(FILE *fp
, struct bcm4908img_info
*info
) {
277 struct bcm4908img_tail
*tail
= &info
->tail
;
278 struct linksys_tail
*linksys
;
279 struct chk_header
*chk
;
288 memset(info
, 0, sizeof(*info
));
292 if (fstat(fileno(fp
), &st
)) {
294 fprintf(stderr
, "Failed to fstat: %d\n", err
);
297 file_size
= st
.st_size
;
299 info
->tail_offset
= file_size
- sizeof(*tail
);
304 if (fread(buf
, 1, sizeof(buf
), fp
) != sizeof(buf
)) {
305 fprintf(stderr
, "Failed to read file header\n");
309 if (be32_to_cpu(chk
->magic
) == 0x2a23245e)
310 info
->cferom_offset
= be32_to_cpu(chk
->header_len
);
312 fseek(fp
, -sizeof(buf
), SEEK_END
);
313 if (fread(buf
, 1, sizeof(buf
), fp
) != sizeof(buf
)) {
314 fprintf(stderr
, "Failed to read file header\n");
317 linksys
= (void *)(buf
+ sizeof(buf
) - sizeof(*linksys
));
318 if (!memcmp(linksys
->magic
, ".LINKSYS.", sizeof(linksys
->magic
))) {
319 info
->tail_offset
-= sizeof(*linksys
);
324 for (info
->bootfs_offset
= info
->cferom_offset
;
325 info
->bootfs_offset
< info
->tail_offset
;
326 info
->bootfs_offset
+= 0x20000) {
327 if (fseek(fp
, info
->bootfs_offset
, SEEK_SET
)) {
329 fprintf(stderr
, "Failed to fseek to the 0x%zx\n", info
->bootfs_offset
);
332 if (fread(&tmp16
, 1, sizeof(tmp16
), fp
) != sizeof(tmp16
)) {
333 fprintf(stderr
, "Failed to read while looking for JFFS2\n");
336 if (be16_to_cpu(tmp16
) == 0x8519)
339 if (info
->bootfs_offset
>= info
->tail_offset
) {
340 fprintf(stderr
, "Failed to find bootfs offset\n");
344 for (info
->rootfs_offset
= info
->bootfs_offset
;
345 info
->rootfs_offset
< info
->tail_offset
;
346 info
->rootfs_offset
+= 0x20000) {
347 uint32_t *magic
= (uint32_t *)&buf
[0];
349 if (fseek(fp
, info
->rootfs_offset
, SEEK_SET
)) {
351 fprintf(stderr
, "Failed to fseek: %d\n", err
);
355 length
= info
->padding_offset
? sizeof(*magic
) : 256;
356 bytes
= fread(buf
, 1, length
, fp
);
357 if (bytes
!= length
) {
358 fprintf(stderr
, "Failed to read %zu bytes\n", length
);
362 if (!info
->padding_offset
&& bcm4908img_is_all_ff(buf
, length
))
363 info
->padding_offset
= info
->rootfs_offset
;
365 if (be32_to_cpu(*magic
) == UBI_EC_HDR_MAGIC
)
368 if (info
->rootfs_offset
>= info
->tail_offset
) {
369 fprintf(stderr
, "Failed to find rootfs offset\n");
375 /* Start with cferom (or bootfs) - skip vendor header */
376 fseek(fp
, info
->cferom_offset
, SEEK_SET
);
378 info
->crc32
= 0xffffffff;
379 length
= info
->tail_offset
- info
->cferom_offset
;
380 while (length
&& (bytes
= fread(buf
, 1, bcm4908img_min(sizeof(buf
), length
), fp
)) > 0) {
381 info
->crc32
= bcm4908img_crc32(info
->crc32
, buf
, bytes
);
385 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
391 if (fread(tail
, 1, sizeof(*tail
), fp
) != sizeof(*tail
)) {
392 fprintf(stderr
, "Failed to read BCM4908 image tail\n");
396 /* Standard validation */
398 if (info
->crc32
!= le32_to_cpu(tail
->crc32
)) {
399 fprintf(stderr
, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info
->crc32
, le32_to_cpu(tail
->crc32
));
406 /**************************************************
408 **************************************************/
410 static int bcm4908img_info(int argc
, char **argv
) {
411 struct bcm4908img_info info
;
412 const char *pathname
= NULL
;
417 while ((c
= getopt(argc
, argv
, "i:")) != -1) {
425 fp
= bcm4908img_open(pathname
, "r");
427 fprintf(stderr
, "Failed to open BCM4908 image\n");
432 err
= bcm4908img_parse(fp
, &info
);
434 fprintf(stderr
, "Failed to parse BCM4908 image\n");
438 if (info
.bootfs_offset
!= info
.cferom_offset
)
439 printf("cferom offset:\t%zu\n", info
.cferom_offset
);
440 printf("bootfs offset:\t0x%zx\n", info
.bootfs_offset
);
441 if (info
.padding_offset
)
442 printf("padding offset:\t0x%zx\n", info
.padding_offset
);
443 printf("rootfs offset:\t0x%zx\n", info
.rootfs_offset
);
444 printf("Checksum:\t0x%08x\n", info
.crc32
);
447 bcm4908img_close(fp
);
452 /**************************************************
454 **************************************************/
456 static ssize_t
bcm4908img_create_append_file(FILE *trx
, const char *in_path
, uint32_t *crc32
) {
462 in
= fopen(in_path
, "r");
464 fprintf(stderr
, "Failed to open %s\n", in_path
);
468 while ((bytes
= fread(buf
, 1, sizeof(buf
), in
)) > 0) {
469 if (fwrite(buf
, 1, bytes
, trx
) != bytes
) {
470 fprintf(stderr
, "Failed to write %zu B to %s\n", bytes
, pathname
);
474 *crc32
= bcm4908img_crc32(*crc32
, buf
, bytes
);
483 static ssize_t
bcm4908img_create_append_zeros(FILE *trx
, size_t length
) {
486 buf
= malloc(length
);
489 memset(buf
, 0, length
);
491 if (fwrite(buf
, 1, length
, trx
) != length
) {
492 fprintf(stderr
, "Failed to write %zu B to %s\n", length
, pathname
);
502 static ssize_t
bcm4908img_create_align(FILE *trx
, size_t cur_offset
, size_t alignment
) {
503 if (cur_offset
& (alignment
- 1)) {
504 size_t length
= alignment
- (cur_offset
% alignment
);
505 return bcm4908img_create_append_zeros(trx
, length
);
511 static int bcm4908img_create(int argc
, char **argv
) {
512 struct bcm4908img_tail tail
= {
513 .version
= cpu_to_le32(WFI_VERSION
),
514 .chip_id
= cpu_to_le32(0x4908),
515 .flash_type
= cpu_to_le32(WFI_NAND128_FLASH
),
516 .flags
= cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM
),
518 uint32_t crc32
= 0xffffffff;
519 size_t cur_offset
= 0;
526 fprintf(stderr
, "No BCM4908 image pathname passed\n");
532 fp
= fopen(pathname
, "w+");
534 fprintf(stderr
, "Failed to open %s\n", pathname
);
540 while ((c
= getopt(argc
, argv
, "f:a:A:")) != -1) {
543 bytes
= bcm4908img_create_append_file(fp
, optarg
, &crc32
);
545 fprintf(stderr
, "Failed to append file %s\n", optarg
);
551 bytes
= bcm4908img_create_align(fp
, cur_offset
, strtol(optarg
, NULL
, 0));
553 fprintf(stderr
, "Failed to append zeros\n");
558 bytes
= strtol(optarg
, NULL
, 0) - cur_offset
;
560 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));
562 bytes
= bcm4908img_create_append_zeros(fp
, bytes
);
564 fprintf(stderr
, "Failed to append zeros\n");
574 tail
.crc32
= cpu_to_le32(crc32
);
576 bytes
= fwrite(&tail
, 1, sizeof(tail
), fp
);
577 if (bytes
!= sizeof(tail
)) {
578 fprintf(stderr
, "Failed to write BCM4908 image tail to %s\n", pathname
);
588 /**************************************************
590 **************************************************/
592 static int bcm4908img_extract(int argc
, char **argv
) {
593 struct bcm4908img_info info
;
594 const char *pathname
= NULL
;
595 const char *type
= NULL
;
604 while ((c
= getopt(argc
, argv
, "i:t:")) != -1) {
615 fp
= bcm4908img_open(pathname
, "r");
617 fprintf(stderr
, "Failed to open BCM4908 image\n");
622 err
= bcm4908img_parse(fp
, &info
);
624 fprintf(stderr
, "Failed to parse BCM4908 image\n");
630 fprintf(stderr
, "No data to extract specified\n");
632 } else if (!strcmp(type
, "cferom")) {
633 offset
= info
.cferom_offset
;
634 length
= info
.bootfs_offset
- offset
;
637 fprintf(stderr
, "This BCM4908 image doesn't contain cferom\n");
640 } else if (!strcmp(type
, "bootfs")) {
641 offset
= info
.bootfs_offset
;
642 length
= (info
.padding_offset
? info
.padding_offset
: info
.rootfs_offset
) - offset
;
643 } else if (!strcmp(type
, "rootfs")) {
644 offset
= info
.rootfs_offset
;
645 length
= info
.tail_offset
- offset
;
646 } else if (!strcmp(type
, "firmware")) {
647 offset
= info
.bootfs_offset
;
648 length
= info
.tail_offset
- offset
;
651 fprintf(stderr
, "Unsupported extract type: %s\n", type
);
657 fprintf(stderr
, "Failed to find requested data in input image\n");
661 fseek(fp
, offset
, SEEK_SET
);
662 while (length
&& (bytes
= fread(buf
, 1, bcm4908img_min(sizeof(buf
), length
), fp
)) > 0) {
663 fwrite(buf
, bytes
, 1, stdout
);
668 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
673 bcm4908img_close(fp
);
678 /**************************************************
680 **************************************************/
682 #define JFFS2_MAGIC_BITMASK 0x1985
684 #define JFFS2_COMPR_NONE 0x00
685 #define JFFS2_COMPR_ZERO 0x01
686 #define JFFS2_COMPR_RTIME 0x02
687 #define JFFS2_COMPR_RUBINMIPS 0x03
688 #define JFFS2_COMPR_COPY 0x04
689 #define JFFS2_COMPR_DYNRUBIN 0x05
690 #define JFFS2_COMPR_ZLIB 0x06
691 #define JFFS2_COMPR_LZO 0x07
692 /* Compatibility flags. */
693 #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
694 #define JFFS2_NODE_ACCURATE 0x2000
695 /* INCOMPAT: Fail to mount the filesystem */
696 #define JFFS2_FEATURE_INCOMPAT 0xc000
697 /* ROCOMPAT: Mount read-only */
698 #define JFFS2_FEATURE_ROCOMPAT 0x8000
699 /* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
700 #define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
701 /* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
702 #define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
704 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
708 } __attribute__((packed
)) jint32_t
;
712 } __attribute__((packed
)) jint16_t
;
714 struct jffs2_unknown_node
716 /* All start like this */
719 jint32_t totlen
; /* So we can skip over nodes we don't grok */
723 struct jffs2_raw_dirent
726 jint16_t nodetype
; /* == JFFS2_NODETYPE_DIRENT */
731 jint32_t ino
; /* == zero for unlink */
741 #define je16_to_cpu(x) ((x).v16)
742 #define je32_to_cpu(x) ((x).v32)
744 static int bcm4908img_bootfs_ls(FILE *fp
, struct bcm4908img_info
*info
) {
745 struct jffs2_unknown_node node
;
746 struct jffs2_raw_dirent dirent
;
751 for (offset
= info
->bootfs_offset
; ; offset
+= (je32_to_cpu(node
.totlen
) + 0x03) & ~0x03) {
752 char name
[FILENAME_MAX
+ 1];
754 if (fseek(fp
, offset
, SEEK_SET
)) {
756 fprintf(stderr
, "Failed to fseek: %d\n", err
);
760 bytes
= fread(&node
, 1, sizeof(node
), fp
);
761 if (bytes
!= sizeof(node
)) {
762 fprintf(stderr
, "Failed to read %zu bytes\n", sizeof(node
));
766 if (je16_to_cpu(node
.magic
) != JFFS2_MAGIC_BITMASK
) {
770 if (je16_to_cpu(node
.nodetype
) != JFFS2_NODETYPE_DIRENT
) {
774 memcpy(&dirent
, &node
, sizeof(node
));
775 bytes
+= fread((uint8_t *)&dirent
+ sizeof(node
), 1, sizeof(dirent
) - sizeof(node
), fp
);
776 if (bytes
!= sizeof(dirent
)) {
777 fprintf(stderr
, "Failed to read %zu bytes\n", sizeof(node
));
781 if (dirent
.nsize
+ 1 > sizeof(name
)) {
782 /* Keep reading & printing BUT exit with error code */
783 fprintf(stderr
, "Too long filename\n");
788 bytes
= fread(name
, 1, dirent
.nsize
, fp
);
789 if (bytes
!= dirent
.nsize
) {
790 fprintf(stderr
, "Failed to read filename\n");
795 printf("%s\n", name
);
801 static int bcm4908img_bootfs_mv(FILE *fp
, struct bcm4908img_info
*info
, int argc
, char **argv
) {
802 struct jffs2_unknown_node node
;
803 struct jffs2_raw_dirent dirent
;
810 if (argc
- optind
< 2) {
811 fprintf(stderr
, "No enough arguments passed\n");
814 oldname
= argv
[optind
++];
815 newname
= argv
[optind
++];
817 if (strlen(newname
) != strlen(oldname
)) {
818 fprintf(stderr
, "New filename must have the same length as the old one\n");
822 for (offset
= info
->bootfs_offset
; ; offset
+= (je32_to_cpu(node
.totlen
) + 0x03) & ~0x03) {
823 char name
[FILENAME_MAX
];
826 if (fseek(fp
, offset
, SEEK_SET
)) {
828 fprintf(stderr
, "Failed to fseek: %d\n", err
);
832 bytes
= fread(&node
, 1, sizeof(node
), fp
);
833 if (bytes
!= sizeof(node
)) {
834 fprintf(stderr
, "Failed to read %zu bytes\n", sizeof(node
));
838 if (je16_to_cpu(node
.magic
) != JFFS2_MAGIC_BITMASK
) {
842 if (je16_to_cpu(node
.nodetype
) != JFFS2_NODETYPE_DIRENT
) {
846 bytes
+= fread((uint8_t *)&dirent
+ sizeof(node
), 1, sizeof(dirent
) - sizeof(node
), fp
);
847 if (bytes
!= sizeof(dirent
)) {
848 fprintf(stderr
, "Failed to read %zu bytes\n", sizeof(node
));
852 if (dirent
.nsize
+ 1 > sizeof(name
)) {
853 fprintf(stderr
, "Too long filename\n");
858 bytes
= fread(name
, 1, dirent
.nsize
, fp
);
859 if (bytes
!= dirent
.nsize
) {
860 fprintf(stderr
, "Failed to read filename\n");
866 printf("offset:%08zx name_crc:%04x filename:%s\n", offset
, je32_to_cpu(dirent
.name_crc
), name
);
868 if (strcmp(name
, oldname
)) {
872 if (fseek(fp
, offset
+ offsetof(struct jffs2_raw_dirent
, name_crc
), SEEK_SET
)) {
874 fprintf(stderr
, "Failed to fseek: %d\n", err
);
877 crc32
= bcm4908img_crc32(0, newname
, dirent
.nsize
);
878 bytes
= fwrite(&crc32
, 1, sizeof(crc32
), fp
);
879 if (bytes
!= sizeof(crc32
)) {
880 fprintf(stderr
, "Failed to write new CRC32\n");
884 if (fseek(fp
, offset
+ offsetof(struct jffs2_raw_dirent
, name
), SEEK_SET
)) {
886 fprintf(stderr
, "Failed to fseek: %d\n", err
);
889 bytes
= fwrite(newname
, 1, dirent
.nsize
, fp
);
890 if (bytes
!= dirent
.nsize
) {
891 fprintf(stderr
, "Failed to write new filename\n");
895 /* Calculate new BCM4908 image checksum */
897 err
= bcm4908img_calc_crc32(fp
, info
);
899 fprintf(stderr
, "Failed to write new filename\n");
903 info
->tail
.crc32
= cpu_to_le32(info
->crc32
);
904 if (fseek(fp
, -sizeof(struct bcm4908img_tail
), SEEK_END
)) {
906 fprintf(stderr
, "Failed to write new filename\n");
910 if (fwrite(&info
->tail
, 1, sizeof(struct bcm4908img_tail
), fp
) != sizeof(struct bcm4908img_tail
)) {
911 fprintf(stderr
, "Failed to write updated tail\n");
915 printf("Successfully renamed %s to the %s\n", oldname
, newname
);
920 fprintf(stderr
, "Failed to find %s\n", oldname
);
925 static int bcm4908img_bootfs(int argc
, char **argv
) {
926 struct bcm4908img_info info
;
927 const char *pathname
= NULL
;
934 while ((c
= getopt(argc
, argv
, "i:")) != -1) {
942 if (argc
- optind
< 1) {
943 fprintf(stderr
, "No bootfs command specified\n");
947 cmd
= argv
[optind
++];
949 mode
= strcmp(cmd
, "mv") ? "r" : "r+";
950 fp
= bcm4908img_open(pathname
, mode
);
952 fprintf(stderr
, "Failed to open BCM4908 image\n");
957 err
= bcm4908img_parse(fp
, &info
);
959 fprintf(stderr
, "Failed to parse BCM4908 image\n");
963 if (!strcmp(cmd
, "ls")) {
964 err
= bcm4908img_bootfs_ls(fp
, &info
);
965 } else if (!strcmp(cmd
, "mv")) {
966 err
= bcm4908img_bootfs_mv(fp
, &info
, argc
, argv
);
969 fprintf(stderr
, "Unsupported bootfs command: %s\n", cmd
);
973 bcm4908img_close(fp
);
978 /**************************************************
980 **************************************************/
982 static void usage() {
985 printf("Info about a BCM4908 image:\n");
986 printf("\tbcm4908img info <options>\n");
987 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
989 printf("Creating a new BCM4908 image:\n");
990 printf("\tbcm4908img create <file> [options]\n");
991 printf("\t-f file\t\t\t\tadd data from specified file\n");
992 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
993 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
995 printf("Extracting from a BCM4908 image:\n");
996 printf("\tbcm4908img extract <options>\n");
997 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
998 printf("\t-t <type>\t\t\t\tone of: cferom, bootfs, rootfs, firmware\n");
1000 printf("Access bootfs in a BCM4908 image:\n");
1001 printf("\tbcm4908img bootfs <options> <command> <arguments>\n");
1002 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
1003 printf("\tls\t\t\t\t\tlist bootfs files\n");
1004 printf("\tmv <source> <dest>\t\t\trename bootfs file\n");
1007 int main(int argc
, char **argv
) {
1010 if (!strcmp(argv
[1], "info"))
1011 return bcm4908img_info(argc
, argv
);
1012 else if (!strcmp(argv
[1], "create"))
1013 return bcm4908img_create(argc
, argv
);
1014 else if (!strcmp(argv
[1], "extract"))
1015 return bcm4908img_extract(argc
, argv
);
1016 else if (!strcmp(argv
[1], "bootfs"))
1017 return bcm4908img_bootfs(argc
, argv
);