firmware-utils: replace GPL 2.0 boilerplate/reference with SPDX
[openwrt/staging/ldir.git] / tools / firmware-utils / src / mktplinkfw-lib.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
4 *
5 * This tool was based on:
6 * TP-Link WR941 V2 firmware checksum fixing tool.
7 * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <unistd.h> /* for unlink() */
15 #include <libgen.h>
16 #include <getopt.h> /* for getopt() */
17 #include <stdarg.h>
18 #include <stdbool.h>
19 #include <endian.h>
20 #include <errno.h>
21 #include <sys/stat.h>
22
23 #include <arpa/inet.h>
24 #include <netinet/in.h>
25
26 #include "mktplinkfw-lib.h"
27 #include "md5.h"
28
29 extern char *ofname;
30 extern char *progname;
31 extern uint32_t kernel_len;
32 extern struct file_info kernel_info;
33 extern struct file_info rootfs_info;
34 extern struct flash_layout *layout;
35 extern uint32_t rootfs_ofs;
36 extern uint32_t rootfs_align;
37 extern int combined;
38 extern int strip_padding;
39 extern int add_jffs2_eof;
40
41 static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
42
43 void fill_header(char *buf, int len);
44
45 struct flash_layout *find_layout(struct flash_layout *layouts, const char *id)
46 {
47 struct flash_layout *ret;
48 struct flash_layout *l;
49
50 ret = NULL;
51 for (l = layouts; l->id != NULL; l++){
52 if (strcasecmp(id, l->id) == 0) {
53 ret = l;
54 break;
55 }
56 };
57
58 return ret;
59 }
60
61 void get_md5(const char *data, int size, uint8_t *md5)
62 {
63 MD5_CTX ctx;
64
65 MD5_Init(&ctx);
66 MD5_Update(&ctx, data, size);
67 MD5_Final(md5, &ctx);
68 }
69
70 int get_file_stat(struct file_info *fdata)
71 {
72 struct stat st;
73 int res;
74
75 if (fdata->file_name == NULL)
76 return 0;
77
78 res = stat(fdata->file_name, &st);
79 if (res){
80 ERRS("stat failed on %s", fdata->file_name);
81 return res;
82 }
83
84 fdata->file_size = st.st_size;
85 return 0;
86 }
87
88 int read_to_buf(const struct file_info *fdata, char *buf)
89 {
90 FILE *f;
91 int ret = EXIT_FAILURE;
92
93 f = fopen(fdata->file_name, "r");
94 if (f == NULL) {
95 ERRS("could not open \"%s\" for reading", fdata->file_name);
96 goto out;
97 }
98
99 errno = 0;
100 fread(buf, fdata->file_size, 1, f);
101 if (errno != 0) {
102 ERRS("unable to read from file \"%s\"", fdata->file_name);
103 goto out_close;
104 }
105
106 ret = EXIT_SUCCESS;
107
108 out_close:
109 fclose(f);
110 out:
111 return ret;
112 }
113
114 static int pad_jffs2(char *buf, int currlen, int maxlen)
115 {
116 int len;
117 uint32_t pad_mask;
118
119 len = currlen;
120 pad_mask = (4 * 1024) | (64 * 1024); /* EOF at 4KB and at 64KB */
121 while ((len < maxlen) && (pad_mask != 0)) {
122 uint32_t mask;
123 int i;
124
125 for (i = 10; i < 32; i++) {
126 mask = 1 << i;
127 if (pad_mask & mask)
128 break;
129 }
130
131 len = ALIGN(len, mask);
132
133 for (i = 10; i < 32; i++) {
134 mask = 1 << i;
135 if ((len & (mask - 1)) == 0)
136 pad_mask &= ~mask;
137 }
138
139 for (i = 0; i < sizeof(jffs2_eof_mark); i++)
140 buf[len + i] = jffs2_eof_mark[i];
141
142 len += sizeof(jffs2_eof_mark);
143 }
144
145 return len;
146 }
147
148 int write_fw(const char *ofname, const char *data, int len)
149 {
150 FILE *f;
151 int ret = EXIT_FAILURE;
152
153 f = fopen(ofname, "w");
154 if (f == NULL) {
155 ERRS("could not open \"%s\" for writing", ofname);
156 goto out;
157 }
158
159 errno = 0;
160 fwrite(data, len, 1, f);
161 if (errno) {
162 ERRS("unable to write output file");
163 goto out_flush;
164 }
165
166 DBG("firmware file \"%s\" completed", ofname);
167
168 ret = EXIT_SUCCESS;
169
170 out_flush:
171 fflush(f);
172 fclose(f);
173 if (ret != EXIT_SUCCESS) {
174 unlink(ofname);
175 }
176 out:
177 return ret;
178 }
179
180 /* Helper functions to inspect_fw() representing different output formats */
181 inline void inspect_fw_pstr(const char *label, const char *str)
182 {
183 printf("%-23s: %s\n", label, str);
184 }
185
186 inline void inspect_fw_phex(const char *label, uint32_t val)
187 {
188 printf("%-23s: 0x%08x\n", label, val);
189 }
190
191 inline void inspect_fw_phexdec(const char *label, uint32_t val)
192 {
193 printf("%-23s: 0x%08x / %8u bytes\n", label, val, val);
194 }
195
196 inline void inspect_fw_pmd5sum(const char *label, const uint8_t *val, const char *text)
197 {
198 int i;
199
200 printf("%-23s:", label);
201 for (i=0; i<MD5SUM_LEN; i++)
202 printf(" %02x", val[i]);
203 printf(" %s\n", text);
204 }
205
206 // header_size = sizeof(struct fw_header)
207 int build_fw(size_t header_size)
208 {
209 int buflen;
210 char *buf;
211 char *p;
212 int ret = EXIT_FAILURE;
213 int writelen = 0;
214
215 writelen = header_size + kernel_len;
216
217 if (combined)
218 buflen = writelen;
219 else
220 buflen = layout->fw_max_len;
221
222 buf = malloc(buflen);
223 if (!buf) {
224 ERR("no memory for buffer\n");
225 goto out;
226 }
227
228 memset(buf, 0xff, buflen);
229 p = buf + header_size;
230 ret = read_to_buf(&kernel_info, p);
231 if (ret)
232 goto out_free_buf;
233
234 if (!combined) {
235 p = buf + rootfs_ofs;
236
237 ret = read_to_buf(&rootfs_info, p);
238 if (ret)
239 goto out_free_buf;
240
241 writelen = rootfs_ofs + rootfs_info.file_size;
242
243 if (add_jffs2_eof)
244 writelen = pad_jffs2(buf, writelen, layout->fw_max_len);
245 }
246
247 if (!strip_padding)
248 writelen = buflen;
249
250 fill_header(buf, writelen);
251 ret = write_fw(ofname, buf, writelen);
252 if (ret)
253 goto out_free_buf;
254
255 ret = EXIT_SUCCESS;
256
257 out_free_buf:
258 free(buf);
259 out:
260 return ret;
261 }