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