tools/bootconfig: Fix to check the write failure correctly
[linux-2.6-microblaze.git] / tools / bootconfig / main.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Boot config tool for initrd image
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <errno.h>
13
14 #include <linux/kernel.h>
15 #include <linux/bootconfig.h>
16
17 static int xbc_show_value(struct xbc_node *node, bool semicolon)
18 {
19         const char *val, *eol;
20         char q;
21         int i = 0;
22
23         eol = semicolon ? ";\n" : "\n";
24         xbc_array_for_each_value(node, val) {
25                 if (strchr(val, '"'))
26                         q = '\'';
27                 else
28                         q = '"';
29                 printf("%c%s%c%s", q, val, q, node->next ? ", " : eol);
30                 i++;
31         }
32         return i;
33 }
34
35 static void xbc_show_compact_tree(void)
36 {
37         struct xbc_node *node, *cnode;
38         int depth = 0, i;
39
40         node = xbc_root_node();
41         while (node && xbc_node_is_key(node)) {
42                 for (i = 0; i < depth; i++)
43                         printf("\t");
44                 cnode = xbc_node_get_child(node);
45                 while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
46                         printf("%s.", xbc_node_get_data(node));
47                         node = cnode;
48                         cnode = xbc_node_get_child(node);
49                 }
50                 if (cnode && xbc_node_is_key(cnode)) {
51                         printf("%s {\n", xbc_node_get_data(node));
52                         depth++;
53                         node = cnode;
54                         continue;
55                 } else if (cnode && xbc_node_is_value(cnode)) {
56                         printf("%s = ", xbc_node_get_data(node));
57                         xbc_show_value(cnode, true);
58                 } else {
59                         printf("%s;\n", xbc_node_get_data(node));
60                 }
61
62                 if (node->next) {
63                         node = xbc_node_get_next(node);
64                         continue;
65                 }
66                 while (!node->next) {
67                         node = xbc_node_get_parent(node);
68                         if (!node)
69                                 return;
70                         if (!xbc_node_get_child(node)->next)
71                                 continue;
72                         depth--;
73                         for (i = 0; i < depth; i++)
74                                 printf("\t");
75                         printf("}\n");
76                 }
77                 node = xbc_node_get_next(node);
78         }
79 }
80
81 static void xbc_show_list(void)
82 {
83         char key[XBC_KEYLEN_MAX];
84         struct xbc_node *leaf;
85         const char *val;
86         int ret = 0;
87
88         xbc_for_each_key_value(leaf, val) {
89                 ret = xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX);
90                 if (ret < 0)
91                         break;
92                 printf("%s = ", key);
93                 if (!val || val[0] == '\0') {
94                         printf("\"\"\n");
95                         continue;
96                 }
97                 xbc_show_value(xbc_node_get_child(leaf), false);
98         }
99 }
100
101 /* Simple real checksum */
102 static int checksum(unsigned char *buf, int len)
103 {
104         int i, sum = 0;
105
106         for (i = 0; i < len; i++)
107                 sum += buf[i];
108
109         return sum;
110 }
111
112 #define PAGE_SIZE       4096
113
114 static int load_xbc_fd(int fd, char **buf, int size)
115 {
116         int ret;
117
118         *buf = malloc(size + 1);
119         if (!*buf)
120                 return -ENOMEM;
121
122         ret = read(fd, *buf, size);
123         if (ret < 0)
124                 return -errno;
125         (*buf)[size] = '\0';
126
127         return ret;
128 }
129
130 /* Return the read size or -errno */
131 static int load_xbc_file(const char *path, char **buf)
132 {
133         struct stat stat;
134         int fd, ret;
135
136         fd = open(path, O_RDONLY);
137         if (fd < 0)
138                 return -errno;
139         ret = fstat(fd, &stat);
140         if (ret < 0)
141                 return -errno;
142
143         ret = load_xbc_fd(fd, buf, stat.st_size);
144
145         close(fd);
146
147         return ret;
148 }
149
150 static int pr_errno(const char *msg, int err)
151 {
152         pr_err("%s: %d\n", msg, err);
153         return err;
154 }
155
156 static int load_xbc_from_initrd(int fd, char **buf)
157 {
158         struct stat stat;
159         int ret;
160         u32 size = 0, csum = 0, rcsum;
161         char magic[BOOTCONFIG_MAGIC_LEN];
162         const char *msg;
163
164         ret = fstat(fd, &stat);
165         if (ret < 0)
166                 return -errno;
167
168         if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN)
169                 return 0;
170
171         if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0)
172                 return pr_errno("Failed to lseek for magic", -errno);
173
174         if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0)
175                 return pr_errno("Failed to read", -errno);
176
177         /* Check the bootconfig magic bytes */
178         if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0)
179                 return 0;
180
181         if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0)
182                 return pr_errno("Failed to lseek for size", -errno);
183
184         if (read(fd, &size, sizeof(u32)) < 0)
185                 return pr_errno("Failed to read size", -errno);
186
187         if (read(fd, &csum, sizeof(u32)) < 0)
188                 return pr_errno("Failed to read checksum", -errno);
189
190         /* Wrong size error  */
191         if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) {
192                 pr_err("bootconfig size is too big\n");
193                 return -E2BIG;
194         }
195
196         if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN),
197                   SEEK_SET) < 0)
198                 return pr_errno("Failed to lseek", -errno);
199
200         ret = load_xbc_fd(fd, buf, size);
201         if (ret < 0)
202                 return ret;
203
204         /* Wrong Checksum */
205         rcsum = checksum((unsigned char *)*buf, size);
206         if (csum != rcsum) {
207                 pr_err("checksum error: %d != %d\n", csum, rcsum);
208                 return -EINVAL;
209         }
210
211         ret = xbc_init(*buf, &msg, NULL);
212         /* Wrong data */
213         if (ret < 0) {
214                 pr_err("parse error: %s.\n", msg);
215                 return ret;
216         }
217
218         return size;
219 }
220
221 static void show_xbc_error(const char *data, const char *msg, int pos)
222 {
223         int lin = 1, col, i;
224
225         if (pos < 0) {
226                 pr_err("Error: %s.\n", msg);
227                 return;
228         }
229
230         /* Note that pos starts from 0 but lin and col should start from 1. */
231         col = pos + 1;
232         for (i = 0; i < pos; i++) {
233                 if (data[i] == '\n') {
234                         lin++;
235                         col = pos - i;
236                 }
237         }
238         pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
239
240 }
241
242 static int init_xbc_with_error(char *buf, int len)
243 {
244         char *copy = strdup(buf);
245         const char *msg;
246         int ret, pos;
247
248         if (!copy)
249                 return -ENOMEM;
250
251         ret = xbc_init(buf, &msg, &pos);
252         if (ret < 0)
253                 show_xbc_error(copy, msg, pos);
254         free(copy);
255
256         return ret;
257 }
258
259 static int show_xbc(const char *path, bool list)
260 {
261         int ret, fd;
262         char *buf = NULL;
263         struct stat st;
264
265         ret = stat(path, &st);
266         if (ret < 0) {
267                 ret = -errno;
268                 pr_err("Failed to stat %s: %d\n", path, ret);
269                 return ret;
270         }
271
272         fd = open(path, O_RDONLY);
273         if (fd < 0) {
274                 ret = -errno;
275                 pr_err("Failed to open initrd %s: %d\n", path, ret);
276                 return ret;
277         }
278
279         ret = load_xbc_from_initrd(fd, &buf);
280         close(fd);
281         if (ret < 0) {
282                 pr_err("Failed to load a boot config from initrd: %d\n", ret);
283                 goto out;
284         }
285         /* Assume a bootconfig file if it is enough small */
286         if (ret == 0 && st.st_size <= XBC_DATA_MAX) {
287                 ret = load_xbc_file(path, &buf);
288                 if (ret < 0) {
289                         pr_err("Failed to load a boot config: %d\n", ret);
290                         goto out;
291                 }
292                 if (init_xbc_with_error(buf, ret) < 0)
293                         goto out;
294         }
295         if (list)
296                 xbc_show_list();
297         else
298                 xbc_show_compact_tree();
299         ret = 0;
300 out:
301         free(buf);
302
303         return ret;
304 }
305
306 static int delete_xbc(const char *path)
307 {
308         struct stat stat;
309         int ret = 0, fd, size;
310         char *buf = NULL;
311
312         fd = open(path, O_RDWR);
313         if (fd < 0) {
314                 ret = -errno;
315                 pr_err("Failed to open initrd %s: %d\n", path, ret);
316                 return ret;
317         }
318
319         size = load_xbc_from_initrd(fd, &buf);
320         if (size < 0) {
321                 ret = size;
322                 pr_err("Failed to load a boot config from initrd: %d\n", ret);
323         } else if (size > 0) {
324                 ret = fstat(fd, &stat);
325                 if (!ret)
326                         ret = ftruncate(fd, stat.st_size
327                                         - size - 8 - BOOTCONFIG_MAGIC_LEN);
328                 if (ret)
329                         ret = -errno;
330         } /* Ignore if there is no boot config in initrd */
331
332         close(fd);
333         free(buf);
334
335         return ret;
336 }
337
338 static int apply_xbc(const char *path, const char *xbc_path)
339 {
340         struct stat stat;
341         u32 size, csum;
342         char *buf, *data;
343         int ret, fd;
344         const char *msg;
345         int pos;
346
347         ret = load_xbc_file(xbc_path, &buf);
348         if (ret < 0) {
349                 pr_err("Failed to load %s : %d\n", xbc_path, ret);
350                 return ret;
351         }
352         size = strlen(buf) + 1;
353         csum = checksum((unsigned char *)buf, size);
354
355         /* Prepare xbc_path data */
356         data = malloc(size + 8);
357         if (!data)
358                 return -ENOMEM;
359         strcpy(data, buf);
360         *(u32 *)(data + size) = size;
361         *(u32 *)(data + size + 4) = csum;
362
363         /* Check the data format */
364         ret = xbc_init(buf, &msg, &pos);
365         if (ret < 0) {
366                 show_xbc_error(data, msg, pos);
367                 free(data);
368                 free(buf);
369
370                 return ret;
371         }
372         printf("Apply %s to %s\n", xbc_path, path);
373         printf("\tNumber of nodes: %d\n", ret);
374         printf("\tSize: %u bytes\n", (unsigned int)size);
375         printf("\tChecksum: %d\n", (unsigned int)csum);
376
377         /* TODO: Check the options by schema */
378         xbc_destroy_all();
379         free(buf);
380
381         /* Remove old boot config if exists */
382         ret = delete_xbc(path);
383         if (ret < 0) {
384                 pr_err("Failed to delete previous boot config: %d\n", ret);
385                 free(data);
386                 return ret;
387         }
388
389         /* Apply new one */
390         fd = open(path, O_RDWR | O_APPEND);
391         if (fd < 0) {
392                 ret = -errno;
393                 pr_err("Failed to open %s: %d\n", path, ret);
394                 free(data);
395                 return ret;
396         }
397         /* TODO: Ensure the @path is initramfs/initrd image */
398         if (fstat(fd, &stat) < 0) {
399                 pr_err("Failed to get the size of %s\n", path);
400                 goto out;
401         }
402         ret = write(fd, data, size + 8);
403         if (ret < size + 8) {
404                 if (ret < 0)
405                         ret = -errno;
406                 pr_err("Failed to apply a boot config: %d\n", ret);
407                 if (ret < 0)
408                         goto out;
409                 goto out_rollback;
410         }
411         /* Write a magic word of the bootconfig */
412         ret = write(fd, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
413         if (ret < BOOTCONFIG_MAGIC_LEN) {
414                 if (ret < 0)
415                         ret = -errno;
416                 pr_err("Failed to apply a boot config magic: %d\n", ret);
417                 goto out_rollback;
418         }
419         ret = 0;
420 out:
421         close(fd);
422         free(data);
423
424         return ret;
425
426 out_rollback:
427         /* Map the partial write to -ENOSPC */
428         if (ret >= 0)
429                 ret = -ENOSPC;
430         if (ftruncate(fd, stat.st_size) < 0) {
431                 ret = -errno;
432                 pr_err("Failed to rollback the write error: %d\n", ret);
433                 pr_err("The initrd %s may be corrupted. Recommend to rebuild.\n", path);
434         }
435         goto out;
436 }
437
438 static int usage(void)
439 {
440         printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
441                 "Or     bootconfig <CONFIG>\n"
442                 " Apply, delete or show boot config to initrd.\n"
443                 " Options:\n"
444                 "               -a <config>: Apply boot config to initrd\n"
445                 "               -d : Delete boot config file from initrd\n"
446                 "               -l : list boot config in initrd or file\n\n"
447                 " If no option is given, show the bootconfig in the given file.\n");
448         return -1;
449 }
450
451 int main(int argc, char **argv)
452 {
453         char *path = NULL;
454         char *apply = NULL;
455         bool delete = false, list = false;
456         int opt;
457
458         while ((opt = getopt(argc, argv, "hda:l")) != -1) {
459                 switch (opt) {
460                 case 'd':
461                         delete = true;
462                         break;
463                 case 'a':
464                         apply = optarg;
465                         break;
466                 case 'l':
467                         list = true;
468                         break;
469                 case 'h':
470                 default:
471                         return usage();
472                 }
473         }
474
475         if ((apply && delete) || (delete && list) || (apply && list)) {
476                 pr_err("Error: You can give one of -a, -d or -l at once.\n");
477                 return usage();
478         }
479
480         if (optind >= argc) {
481                 pr_err("Error: No initrd is specified.\n");
482                 return usage();
483         }
484
485         path = argv[optind];
486
487         if (apply)
488                 return apply_xbc(path, apply);
489         else if (delete)
490                 return delete_xbc(path);
491
492         return show_xbc(path, list);
493 }