Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Boot config tool for initrd image
0004  */
0005 #include <stdio.h>
0006 #include <stdlib.h>
0007 #include <sys/types.h>
0008 #include <sys/stat.h>
0009 #include <fcntl.h>
0010 #include <unistd.h>
0011 #include <string.h>
0012 #include <errno.h>
0013 #include <endian.h>
0014 
0015 #include <linux/bootconfig.h>
0016 
0017 #define pr_err(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
0018 
0019 static int xbc_show_value(struct xbc_node *node, bool semicolon)
0020 {
0021     const char *val, *eol;
0022     char q;
0023     int i = 0;
0024 
0025     eol = semicolon ? ";\n" : "\n";
0026     xbc_array_for_each_value(node, val) {
0027         if (strchr(val, '"'))
0028             q = '\'';
0029         else
0030             q = '"';
0031         printf("%c%s%c%s", q, val, q, xbc_node_is_array(node) ? ", " : eol);
0032         i++;
0033     }
0034     return i;
0035 }
0036 
0037 static void xbc_show_compact_tree(void)
0038 {
0039     struct xbc_node *node, *cnode = NULL, *vnode;
0040     int depth = 0, i;
0041 
0042     node = xbc_root_node();
0043     while (node && xbc_node_is_key(node)) {
0044         for (i = 0; i < depth; i++)
0045             printf("\t");
0046         if (!cnode)
0047             cnode = xbc_node_get_child(node);
0048         while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
0049             vnode = xbc_node_get_child(cnode);
0050             /*
0051              * If @cnode has value and subkeys, this
0052              * should show it as below.
0053              *
0054              * key(@node) {
0055              *      key(@cnode) = value;
0056              *      key(@cnode) {
0057              *          subkeys;
0058              *      }
0059              * }
0060              */
0061             if (vnode && xbc_node_is_value(vnode) && vnode->next)
0062                 break;
0063             printf("%s.", xbc_node_get_data(node));
0064             node = cnode;
0065             cnode = vnode;
0066         }
0067         if (cnode && xbc_node_is_key(cnode)) {
0068             printf("%s {\n", xbc_node_get_data(node));
0069             depth++;
0070             node = cnode;
0071             cnode = NULL;
0072             continue;
0073         } else if (cnode && xbc_node_is_value(cnode)) {
0074             printf("%s = ", xbc_node_get_data(node));
0075             xbc_show_value(cnode, true);
0076             /*
0077              * If @node has value and subkeys, continue
0078              * looping on subkeys with same node.
0079              */
0080             if (cnode->next) {
0081                 cnode = xbc_node_get_next(cnode);
0082                 continue;
0083             }
0084         } else {
0085             printf("%s;\n", xbc_node_get_data(node));
0086         }
0087         cnode = NULL;
0088 
0089         if (node->next) {
0090             node = xbc_node_get_next(node);
0091             continue;
0092         }
0093         while (!node->next) {
0094             node = xbc_node_get_parent(node);
0095             if (!node)
0096                 return;
0097             if (!xbc_node_get_child(node)->next)
0098                 continue;
0099             if (depth) {
0100                 depth--;
0101                 for (i = 0; i < depth; i++)
0102                     printf("\t");
0103                 printf("}\n");
0104             }
0105         }
0106         node = xbc_node_get_next(node);
0107     }
0108 }
0109 
0110 static void xbc_show_list(void)
0111 {
0112     char key[XBC_KEYLEN_MAX];
0113     struct xbc_node *leaf;
0114     const char *val;
0115     int ret;
0116 
0117     xbc_for_each_key_value(leaf, val) {
0118         ret = xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX);
0119         if (ret < 0) {
0120             fprintf(stderr, "Failed to compose key %d\n", ret);
0121             break;
0122         }
0123         printf("%s = ", key);
0124         if (!val || val[0] == '\0') {
0125             printf("\"\"\n");
0126             continue;
0127         }
0128         xbc_show_value(xbc_node_get_child(leaf), false);
0129     }
0130 }
0131 
0132 #define PAGE_SIZE   4096
0133 
0134 static int load_xbc_fd(int fd, char **buf, int size)
0135 {
0136     int ret;
0137 
0138     *buf = malloc(size + 1);
0139     if (!*buf)
0140         return -ENOMEM;
0141 
0142     ret = read(fd, *buf, size);
0143     if (ret < 0)
0144         return -errno;
0145     (*buf)[size] = '\0';
0146 
0147     return ret;
0148 }
0149 
0150 /* Return the read size or -errno */
0151 static int load_xbc_file(const char *path, char **buf)
0152 {
0153     struct stat stat;
0154     int fd, ret;
0155 
0156     fd = open(path, O_RDONLY);
0157     if (fd < 0)
0158         return -errno;
0159     ret = fstat(fd, &stat);
0160     if (ret < 0)
0161         return -errno;
0162 
0163     ret = load_xbc_fd(fd, buf, stat.st_size);
0164 
0165     close(fd);
0166 
0167     return ret;
0168 }
0169 
0170 static int pr_errno(const char *msg, int err)
0171 {
0172     pr_err("%s: %d\n", msg, err);
0173     return err;
0174 }
0175 
0176 static int load_xbc_from_initrd(int fd, char **buf)
0177 {
0178     struct stat stat;
0179     int ret;
0180     uint32_t size = 0, csum = 0, rcsum;
0181     char magic[BOOTCONFIG_MAGIC_LEN];
0182     const char *msg;
0183 
0184     ret = fstat(fd, &stat);
0185     if (ret < 0)
0186         return -errno;
0187 
0188     if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN)
0189         return 0;
0190 
0191     if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0)
0192         return pr_errno("Failed to lseek for magic", -errno);
0193 
0194     if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0)
0195         return pr_errno("Failed to read", -errno);
0196 
0197     /* Check the bootconfig magic bytes */
0198     if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0)
0199         return 0;
0200 
0201     if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0)
0202         return pr_errno("Failed to lseek for size", -errno);
0203 
0204     if (read(fd, &size, sizeof(uint32_t)) < 0)
0205         return pr_errno("Failed to read size", -errno);
0206     size = le32toh(size);
0207 
0208     if (read(fd, &csum, sizeof(uint32_t)) < 0)
0209         return pr_errno("Failed to read checksum", -errno);
0210     csum = le32toh(csum);
0211 
0212     /* Wrong size error  */
0213     if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) {
0214         pr_err("bootconfig size is too big\n");
0215         return -E2BIG;
0216     }
0217 
0218     if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN),
0219           SEEK_SET) < 0)
0220         return pr_errno("Failed to lseek", -errno);
0221 
0222     ret = load_xbc_fd(fd, buf, size);
0223     if (ret < 0)
0224         return ret;
0225 
0226     /* Wrong Checksum */
0227     rcsum = xbc_calc_checksum(*buf, size);
0228     if (csum != rcsum) {
0229         pr_err("checksum error: %d != %d\n", csum, rcsum);
0230         return -EINVAL;
0231     }
0232 
0233     ret = xbc_init(*buf, size, &msg, NULL);
0234     /* Wrong data */
0235     if (ret < 0) {
0236         pr_err("parse error: %s.\n", msg);
0237         return ret;
0238     }
0239 
0240     return size;
0241 }
0242 
0243 static void show_xbc_error(const char *data, const char *msg, int pos)
0244 {
0245     int lin = 1, col, i;
0246 
0247     if (pos < 0) {
0248         pr_err("Error: %s.\n", msg);
0249         return;
0250     }
0251 
0252     /* Note that pos starts from 0 but lin and col should start from 1. */
0253     col = pos + 1;
0254     for (i = 0; i < pos; i++) {
0255         if (data[i] == '\n') {
0256             lin++;
0257             col = pos - i;
0258         }
0259     }
0260     pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
0261 
0262 }
0263 
0264 static int init_xbc_with_error(char *buf, int len)
0265 {
0266     char *copy = strdup(buf);
0267     const char *msg;
0268     int ret, pos;
0269 
0270     if (!copy)
0271         return -ENOMEM;
0272 
0273     ret = xbc_init(buf, len, &msg, &pos);
0274     if (ret < 0)
0275         show_xbc_error(copy, msg, pos);
0276     free(copy);
0277 
0278     return ret;
0279 }
0280 
0281 static int show_xbc(const char *path, bool list)
0282 {
0283     int ret, fd;
0284     char *buf = NULL;
0285     struct stat st;
0286 
0287     ret = stat(path, &st);
0288     if (ret < 0) {
0289         ret = -errno;
0290         pr_err("Failed to stat %s: %d\n", path, ret);
0291         return ret;
0292     }
0293 
0294     fd = open(path, O_RDONLY);
0295     if (fd < 0) {
0296         ret = -errno;
0297         pr_err("Failed to open initrd %s: %d\n", path, ret);
0298         return ret;
0299     }
0300 
0301     ret = load_xbc_from_initrd(fd, &buf);
0302     close(fd);
0303     if (ret < 0) {
0304         pr_err("Failed to load a boot config from initrd: %d\n", ret);
0305         goto out;
0306     }
0307     /* Assume a bootconfig file if it is enough small */
0308     if (ret == 0 && st.st_size <= XBC_DATA_MAX) {
0309         ret = load_xbc_file(path, &buf);
0310         if (ret < 0) {
0311             pr_err("Failed to load a boot config: %d\n", ret);
0312             goto out;
0313         }
0314         if (init_xbc_with_error(buf, ret) < 0)
0315             goto out;
0316     }
0317     if (list)
0318         xbc_show_list();
0319     else
0320         xbc_show_compact_tree();
0321     ret = 0;
0322 out:
0323     free(buf);
0324 
0325     return ret;
0326 }
0327 
0328 static int delete_xbc(const char *path)
0329 {
0330     struct stat stat;
0331     int ret = 0, fd, size;
0332     char *buf = NULL;
0333 
0334     fd = open(path, O_RDWR);
0335     if (fd < 0) {
0336         ret = -errno;
0337         pr_err("Failed to open initrd %s: %d\n", path, ret);
0338         return ret;
0339     }
0340 
0341     size = load_xbc_from_initrd(fd, &buf);
0342     if (size < 0) {
0343         ret = size;
0344         pr_err("Failed to load a boot config from initrd: %d\n", ret);
0345     } else if (size > 0) {
0346         ret = fstat(fd, &stat);
0347         if (!ret)
0348             ret = ftruncate(fd, stat.st_size
0349                     - size - 8 - BOOTCONFIG_MAGIC_LEN);
0350         if (ret)
0351             ret = -errno;
0352     } /* Ignore if there is no boot config in initrd */
0353 
0354     close(fd);
0355     free(buf);
0356 
0357     return ret;
0358 }
0359 
0360 static int apply_xbc(const char *path, const char *xbc_path)
0361 {
0362     char *buf, *data, *p;
0363     size_t total_size;
0364     struct stat stat;
0365     const char *msg;
0366     uint32_t size, csum;
0367     int pos, pad;
0368     int ret, fd;
0369 
0370     ret = load_xbc_file(xbc_path, &buf);
0371     if (ret < 0) {
0372         pr_err("Failed to load %s : %d\n", xbc_path, ret);
0373         return ret;
0374     }
0375     size = strlen(buf) + 1;
0376     csum = xbc_calc_checksum(buf, size);
0377 
0378     /* Backup the bootconfig data */
0379     data = calloc(size + BOOTCONFIG_ALIGN +
0380               sizeof(uint32_t) + sizeof(uint32_t) + BOOTCONFIG_MAGIC_LEN, 1);
0381     if (!data)
0382         return -ENOMEM;
0383     memcpy(data, buf, size);
0384 
0385     /* Check the data format */
0386     ret = xbc_init(buf, size, &msg, &pos);
0387     if (ret < 0) {
0388         show_xbc_error(data, msg, pos);
0389         free(data);
0390         free(buf);
0391 
0392         return ret;
0393     }
0394     printf("Apply %s to %s\n", xbc_path, path);
0395     xbc_get_info(&ret, NULL);
0396     printf("\tNumber of nodes: %d\n", ret);
0397     printf("\tSize: %u bytes\n", (unsigned int)size);
0398     printf("\tChecksum: %d\n", (unsigned int)csum);
0399 
0400     /* TODO: Check the options by schema */
0401     xbc_exit();
0402     free(buf);
0403 
0404     /* Remove old boot config if exists */
0405     ret = delete_xbc(path);
0406     if (ret < 0) {
0407         pr_err("Failed to delete previous boot config: %d\n", ret);
0408         free(data);
0409         return ret;
0410     }
0411 
0412     /* Apply new one */
0413     fd = open(path, O_RDWR | O_APPEND);
0414     if (fd < 0) {
0415         ret = -errno;
0416         pr_err("Failed to open %s: %d\n", path, ret);
0417         free(data);
0418         return ret;
0419     }
0420     /* TODO: Ensure the @path is initramfs/initrd image */
0421     if (fstat(fd, &stat) < 0) {
0422         ret = -errno;
0423         pr_err("Failed to get the size of %s\n", path);
0424         goto out;
0425     }
0426 
0427     /* To align up the total size to BOOTCONFIG_ALIGN, get padding size */
0428     total_size = stat.st_size + size + sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN;
0429     pad = ((total_size + BOOTCONFIG_ALIGN - 1) & (~BOOTCONFIG_ALIGN_MASK)) - total_size;
0430     size += pad;
0431 
0432     /* Add a footer */
0433     p = data + size;
0434     *(uint32_t *)p = htole32(size);
0435     p += sizeof(uint32_t);
0436 
0437     *(uint32_t *)p = htole32(csum);
0438     p += sizeof(uint32_t);
0439 
0440     memcpy(p, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
0441     p += BOOTCONFIG_MAGIC_LEN;
0442 
0443     total_size = p - data;
0444 
0445     ret = write(fd, data, total_size);
0446     if (ret < total_size) {
0447         if (ret < 0)
0448             ret = -errno;
0449         pr_err("Failed to apply a boot config: %d\n", ret);
0450         if (ret >= 0)
0451             goto out_rollback;
0452     } else
0453         ret = 0;
0454 
0455 out:
0456     close(fd);
0457     free(data);
0458 
0459     return ret;
0460 
0461 out_rollback:
0462     /* Map the partial write to -ENOSPC */
0463     if (ret >= 0)
0464         ret = -ENOSPC;
0465     if (ftruncate(fd, stat.st_size) < 0) {
0466         ret = -errno;
0467         pr_err("Failed to rollback the write error: %d\n", ret);
0468         pr_err("The initrd %s may be corrupted. Recommend to rebuild.\n", path);
0469     }
0470     goto out;
0471 }
0472 
0473 static int usage(void)
0474 {
0475     printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
0476         "Or     bootconfig <CONFIG>\n"
0477         " Apply, delete or show boot config to initrd.\n"
0478         " Options:\n"
0479         "       -a <config>: Apply boot config to initrd\n"
0480         "       -d : Delete boot config file from initrd\n"
0481         "       -l : list boot config in initrd or file\n\n"
0482         " If no option is given, show the bootconfig in the given file.\n");
0483     return -1;
0484 }
0485 
0486 int main(int argc, char **argv)
0487 {
0488     char *path = NULL;
0489     char *apply = NULL;
0490     bool delete = false, list = false;
0491     int opt;
0492 
0493     while ((opt = getopt(argc, argv, "hda:l")) != -1) {
0494         switch (opt) {
0495         case 'd':
0496             delete = true;
0497             break;
0498         case 'a':
0499             apply = optarg;
0500             break;
0501         case 'l':
0502             list = true;
0503             break;
0504         case 'h':
0505         default:
0506             return usage();
0507         }
0508     }
0509 
0510     if ((apply && delete) || (delete && list) || (apply && list)) {
0511         pr_err("Error: You can give one of -a, -d or -l at once.\n");
0512         return usage();
0513     }
0514 
0515     if (optind >= argc) {
0516         pr_err("Error: No initrd is specified.\n");
0517         return usage();
0518     }
0519 
0520     path = argv[optind];
0521 
0522     if (apply)
0523         return apply_xbc(path, apply);
0524     else if (delete)
0525         return delete_xbc(path);
0526 
0527     return show_xbc(path, list);
0528 }