Newer
Older
* fdtextract -- Tool to extract sub images from FIT image.
*
* Copyright (C) 2021 IOPSYS Software Solutions AB. All rights reserved.
*
* Author: jonas.hoglund@iopsys.eu
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
static const char usage_synopsis[] = "fdtextract [options] <file>";
static const char usage_short_opts[] = "le:o:s:a:i:z:" USAGE_COMMON_SHORT_OPTS;
static struct option const usage_long_opts[] = {
{"list", no_argument, NULL, 'l'},
{"extract", a_argument, NULL, 'e'},
{"out", a_argument, NULL, 'o'},
{"hash", a_argument, NULL, 's'},
{"attribute", a_argument, NULL, 'a'},
{"image", a_argument, NULL, 'i'},
{"size", a_argument, NULL, 'z'},
USAGE_COMMON_LONG_OPTS
};
static const char * const usage_opts_help[] = {
"List images embedded in FIT",
"Extract image from FIT",
"Output image name",
"Use image in FIT to get attribute",
"Size of embedded image in FIT",
USAGE_COMMON_OPTS_HELP
static int list_images(char *buf)
{
int ndepth = 0, count = 0, noffset;
const char *name;
noffset = fdt_next_node(buf, noffset, &ndepth);
while (ndepth > 0) {
if (ndepth == 1) {
name = fdt_get_name(buf, noffset, NULL);
printf("[%d]: %s\n", count, name);
count++;
}
noffset = fdt_next_node(buf, noffset, &ndepth);
}
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* Print the size of an image embedded in the FIT. */
static int get_size(char *buf, char *name)
{
char path[MAX_PATH_LEN] = {0};
int noffset, data_size;
const fdt32_t *val;
snprintf(path, MAX_PATH_LEN, "/images/%s", name);
noffset = fdt_path_offset(buf, path);
if (noffset < 0) {
fprintf(stderr, "Error: could not find image: %s.\n", name);
return -1;
}
val = fdt_getprop(buf, noffset, "data-size", NULL);
if (val) {
data_size = fdt32_to_cpu(*val);
} else {
fdt_getprop(buf, noffset, "data", &data_size);
}
if (data_size < 0) {
fprintf(stderr, "Error: Could not get image data size: %s.\n", name);
return -1;
}
printf("%d\n", data_size);
return 0;
}
static ssize_t copy_data(int out_fd, int in_fd, ssize_t size)
{
ssize_t left = size;
if (left < 0) {
errno = EINVAL;
return -1;
}
while (left > 0) {
ssize_t written = sendfile(out_fd, in_fd, NULL, left);
if (written < 0 && errno == EINVAL)
break;
if (written < 0)
return -1;
if (written == 0)
return size - left;
left -= written;
}
while (left > 0) {
char buf[4096];
ssize_t count = (ssize_t)sizeof(buf) < left ? (ssize_t)sizeof(buf) : left;
ssize_t written;
count = read(in_fd, buf, count);
if (count < 0)
return -1;
if (count == 0)
break;
written = write(out_fd, buf, count);
if (written < 0)
return -1;
left -= written;
if (written < count)
break;
}
return size - left;
}
/* Extract an image embedded in the FIT. */
static int extract_image(char *buf, char *name, int in_fd, int out_fd)
char path[MAX_PATH_LEN] = {0};
int noffset, count, data_size;
bool is_external = false;
snprintf(path, MAX_PATH_LEN, "/images/%s", name);
noffset = fdt_path_offset(buf, path);
if (noffset < 0) {
fprintf(stderr, "Error: could not find image: %s.\n", name);
/* Get offset of image. Try both relative and absolute offset. */
if ((val = fdt_getprop(buf, noffset, "data-offset", NULL))) {
/* Relative offset */
data_offset = fdt32_to_cpu(*val);
data_offset += ((fdt_totalsize(buf) + 3) & ~3);
is_external = true;
} else if ((val = fdt_getprop(buf, noffset, "data-position", NULL))) {
if (is_external) {
/* Size */
val = fdt_getprop(buf, noffset, "data-size", NULL);
if (!val) {
fprintf(stderr, "Error: Could not get image size: %s.\n", name);
return -1;
}
data_size = fdt32_to_cpu(*val);
if (lseek(in_fd, data_offset, SEEK_SET) < 0) {
fprintf(stderr, "Error: Could not lseek to: %u\n", data_offset);
return errno;
}
count = copy_data(out_fd, in_fd, data_size);
const char *data = fdt_getprop(buf, noffset, "data", &data_size);
if (data_size < 0) {
fprintf(stderr, "Error: Could not get image data: %s.\n", name);
return -1;
}
count = write(out_fd, data, data_size);
if (count < 0) {
fprintf(stderr, "Error: I/O error while copying image data.\n");
return errno;
fprintf(stderr, "Error: Image data was truncated.\n");
return -1;
/* Print the sha256 hash on an image embedded in the FIT. */
char path[MAX_PATH_LEN] = {0};
snprintf(path, MAX_PATH_LEN, "/images/%s", name);
noffset = fdt_path_offset(buf, path);
if (noffset < 0) {
fprintf(stderr, "Error: could not find image: %s.\n", name);
for (noffset = fdt_first_subnode(buf, noffset);
noffset >= 0;
noffset = fdt_next_subnode(buf, noffset)) {
/* Check subnode name, must start with "hash" */
const char *node_name = fdt_get_name(buf, noffset, NULL);
if (!strncmp(node_name, FIT_HASH_NODENAME,
strlen(FIT_HASH_NODENAME))) {
/* Verify that we know the hash algo. */
algo = fdt_getprop(buf, noffset, "algo", NULL);
if (algo && !strcmp(algo, "sha256")) {
val = (uint8_t *)fdt_getprop(buf, noffset, "value", NULL);
break;
}
}
}
if (!val) {
fprintf(stderr, "Error: No suitable hash found for image %s.\n", name);
/* Print out an attribute of the root node. */
static int get_attribute(char *buf, char *name, char *imagename)
{
int noffset;
const char *val = NULL;
char path[MAX_PATH_LEN] = "/";
snprintf(path, MAX_PATH_LEN, "/images/%s", imagename);
noffset = fdt_path_offset(buf, path);
fprintf(stderr, "Error: invalid FDT path: %s\n", path);
return -1;
}
/* Print the property value. */
val = fdt_getprop(buf, noffset, name, NULL);
if (!val) {
fprintf(stderr, "Error: could not find property %s.\n", name);
return -1;
}
printf("%s\n", val);
return 0;
}
char *read_header(int fd)
{
ssize_t len, total_size;
/* Read minimal static struct */
buf = malloc(FDT_V1_SIZE);
if (!buf)
return NULL;
len = read(fd, buf, FDT_V1_SIZE);
return NULL;
/* Read rest of header */
total_size = fdt_totalsize(buf);
free(buf);
return NULL;
}
tmp = realloc(buf, total_size);
if (total_size && !tmp) {
free(buf);
return NULL;
len = read(fd, buf + FDT_V1_SIZE, total_size - FDT_V1_SIZE);
return NULL;
return buf;
}
char *buf, *name = NULL, *out = NULL, *imagename = NULL;
hash = false, attribute = false, size = false;
int in_fd, out_fd = STDOUT_FILENO;
while ((opt = util_getopt_long()) != EOF) {
if ((opt == 'l' || opt == 'e' || opt == 's' || opt == 'a') &&
(list || extract || hash || attribute))
usage("only one of --list/--extract/--hash/--attribute allowed");
switch (opt) {
case_USAGE_COMMON_FLAGS
case 'l':
list = true;
break;
case 'z':
size = true;
name = optarg;
break;
case 'e':
extract = true;
break;
case 'o':
break;
case 's':
break;
case 'a':
break;
case 'i':
break;
}
}
if (optind != argc - 1)
usage("missing input filename");
file = argv[optind];
in_fd = open(file, O_RDONLY);
if (in_fd < 0) {
die("could not open: %s\n", file);
}
buf = read_header(in_fd);
if (!buf) {
die("could not read header from: %s\n", file);
if (imagename != NULL && attribute == false) {
die("--image should be used with --attribute\n");
}
if (out != NULL && extract == false) {
die("--out should be used with --extract\n");
}
if (out && (out_fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
die("could not open output file: %s\n", out);
}
/* Pass the pointer to the header to read attributes. */
if (size)
ret = get_size(buf, name);
ret = get_attribute(buf, name, imagename);
/* Let extract image read the entire file. */
if (extract)
ret = extract_image(buf, name, in_fd, out_fd);
if (out_fd != STDOUT_FILENO)
close(out_fd);
free(buf);
close(in_fd);
return ret ? 1 : 0;