linux-stable/tools/bootconfig/main.c
Masami Hiramatsu 0f0d0a77cc tools/bootconfig: Show the number of bootconfig nodes
Show the number of bootconfig nodes when applying new bootconfig to
initrd.

Since there are limitations of bootconfig not only in its filesize,
but also the number of nodes, the number should be shown when applying
so that user can get the feeling of scale of current bootconfig.

Link: http://lkml.kernel.org/r/158091061337.27924.10886706631693823982.stgit@devnote2

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
2020-02-05 17:17:05 -05:00

354 lines
6.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Boot config tool for initrd image
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/kernel.h>
#include <linux/bootconfig.h>
int pr_output = 1;
static int xbc_show_array(struct xbc_node *node)
{
const char *val;
int i = 0;
xbc_array_for_each_value(node, val) {
printf("\"%s\"%s", val, node->next ? ", " : ";\n");
i++;
}
return i;
}
static void xbc_show_compact_tree(void)
{
struct xbc_node *node, *cnode;
int depth = 0, i;
node = xbc_root_node();
while (node && xbc_node_is_key(node)) {
for (i = 0; i < depth; i++)
printf("\t");
cnode = xbc_node_get_child(node);
while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
printf("%s.", xbc_node_get_data(node));
node = cnode;
cnode = xbc_node_get_child(node);
}
if (cnode && xbc_node_is_key(cnode)) {
printf("%s {\n", xbc_node_get_data(node));
depth++;
node = cnode;
continue;
} else if (cnode && xbc_node_is_value(cnode)) {
printf("%s = ", xbc_node_get_data(node));
if (cnode->next)
xbc_show_array(cnode);
else
printf("\"%s\";\n", xbc_node_get_data(cnode));
} else {
printf("%s;\n", xbc_node_get_data(node));
}
if (node->next) {
node = xbc_node_get_next(node);
continue;
}
while (!node->next) {
node = xbc_node_get_parent(node);
if (!node)
return;
if (!xbc_node_get_child(node)->next)
continue;
depth--;
for (i = 0; i < depth; i++)
printf("\t");
printf("}\n");
}
node = xbc_node_get_next(node);
}
}
/* Simple real checksum */
int checksum(unsigned char *buf, int len)
{
int i, sum = 0;
for (i = 0; i < len; i++)
sum += buf[i];
return sum;
}
#define PAGE_SIZE 4096
int load_xbc_fd(int fd, char **buf, int size)
{
int ret;
*buf = malloc(size + 1);
if (!*buf)
return -ENOMEM;
ret = read(fd, *buf, size);
if (ret < 0)
return -errno;
(*buf)[size] = '\0';
return ret;
}
/* Return the read size or -errno */
int load_xbc_file(const char *path, char **buf)
{
struct stat stat;
int fd, ret;
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
ret = fstat(fd, &stat);
if (ret < 0)
return -errno;
ret = load_xbc_fd(fd, buf, stat.st_size);
close(fd);
return ret;
}
int load_xbc_from_initrd(int fd, char **buf)
{
struct stat stat;
int ret;
u32 size = 0, csum = 0, rcsum;
ret = fstat(fd, &stat);
if (ret < 0)
return -errno;
if (stat.st_size < 8)
return 0;
if (lseek(fd, -8, SEEK_END) < 0) {
printf("Failed to lseek: %d\n", -errno);
return -errno;
}
if (read(fd, &size, sizeof(u32)) < 0)
return -errno;
if (read(fd, &csum, sizeof(u32)) < 0)
return -errno;
/* Wrong size, maybe no boot config here */
if (stat.st_size < size + 8)
return 0;
if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) {
printf("Failed to lseek: %d\n", -errno);
return -errno;
}
ret = load_xbc_fd(fd, buf, size);
if (ret < 0)
return ret;
/* Wrong Checksum, maybe no boot config here */
rcsum = checksum((unsigned char *)*buf, size);
if (csum != rcsum) {
printf("checksum error: %d != %d\n", csum, rcsum);
return 0;
}
ret = xbc_init(*buf);
/* Wrong data, maybe no boot config here */
if (ret < 0)
return 0;
return size;
}
int show_xbc(const char *path)
{
int ret, fd;
char *buf = NULL;
fd = open(path, O_RDONLY);
if (fd < 0) {
printf("Failed to open initrd %s: %d\n", path, fd);
return -errno;
}
ret = load_xbc_from_initrd(fd, &buf);
if (ret < 0)
printf("Failed to load a boot config from initrd: %d\n", ret);
else
xbc_show_compact_tree();
close(fd);
free(buf);
return ret;
}
int delete_xbc(const char *path)
{
struct stat stat;
int ret = 0, fd, size;
char *buf = NULL;
fd = open(path, O_RDWR);
if (fd < 0) {
printf("Failed to open initrd %s: %d\n", path, fd);
return -errno;
}
/*
* Suppress error messages in xbc_init() because it can be just a
* data which concidentally matches the size and checksum footer.
*/
pr_output = 0;
size = load_xbc_from_initrd(fd, &buf);
pr_output = 1;
if (size < 0) {
ret = size;
printf("Failed to load a boot config from initrd: %d\n", ret);
} else if (size > 0) {
ret = fstat(fd, &stat);
if (!ret)
ret = ftruncate(fd, stat.st_size - size - 8);
if (ret)
ret = -errno;
} /* Ignore if there is no boot config in initrd */
close(fd);
free(buf);
return ret;
}
int apply_xbc(const char *path, const char *xbc_path)
{
u32 size, csum;
char *buf, *data;
int ret, fd;
ret = load_xbc_file(xbc_path, &buf);
if (ret < 0) {
printf("Failed to load %s : %d\n", xbc_path, ret);
return ret;
}
size = strlen(buf) + 1;
csum = checksum((unsigned char *)buf, size);
/* Prepare xbc_path data */
data = malloc(size + 8);
if (!data)
return -ENOMEM;
strcpy(data, buf);
*(u32 *)(data + size) = size;
*(u32 *)(data + size + 4) = csum;
/* Check the data format */
ret = xbc_init(buf);
if (ret < 0) {
printf("Failed to parse %s: %d\n", xbc_path, ret);
free(data);
free(buf);
return ret;
}
printf("Apply %s to %s\n", xbc_path, path);
printf("\tNumber of nodes: %d\n", ret);
printf("\tSize: %u bytes\n", (unsigned int)size);
printf("\tChecksum: %d\n", (unsigned int)csum);
/* TODO: Check the options by schema */
xbc_destroy_all();
free(buf);
/* Remove old boot config if exists */
ret = delete_xbc(path);
if (ret < 0) {
printf("Failed to delete previous boot config: %d\n", ret);
return ret;
}
/* Apply new one */
fd = open(path, O_RDWR | O_APPEND);
if (fd < 0) {
printf("Failed to open %s: %d\n", path, fd);
return fd;
}
/* TODO: Ensure the @path is initramfs/initrd image */
ret = write(fd, data, size + 8);
if (ret < 0) {
printf("Failed to apply a boot config: %d\n", ret);
return ret;
}
close(fd);
free(data);
return 0;
}
int usage(void)
{
printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
" Apply, delete or show boot config to initrd.\n"
" Options:\n"
" -a <config>: Apply boot config to initrd\n"
" -d : Delete boot config file from initrd\n\n"
" If no option is given, show current applied boot config.\n");
return -1;
}
int main(int argc, char **argv)
{
char *path = NULL;
char *apply = NULL;
bool delete = false;
int opt;
while ((opt = getopt(argc, argv, "hda:")) != -1) {
switch (opt) {
case 'd':
delete = true;
break;
case 'a':
apply = optarg;
break;
case 'h':
default:
return usage();
}
}
if (apply && delete) {
printf("Error: You can not specify both -a and -d at once.\n");
return usage();
}
if (optind >= argc) {
printf("Error: No initrd is specified.\n");
return usage();
}
path = argv[optind];
if (apply)
return apply_xbc(path, apply);
else if (delete)
return delete_xbc(path);
return show_xbc(path);
}