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