forked from luck/tmp_suning_uos_patched
0f0d0a77cc
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>
355 lines
6.7 KiB
C
355 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);
|
|
}
|