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