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