[0.2.0] Rewrite the whole library

* Rewrite libbootimg to use "blobs" concept - kernel, initrd,
  second stage and DTB are all objects of same type, which
  enabled me to make functions shorter (they don't have to deal
  with each of them individualy).
* Move image size and config related functions to bbootimg.c - they
  should not be in the library.
* Document all library functions
* Clean-up the API
This commit is contained in:
Vojtech Bocek
2014-02-06 18:09:13 +01:00
parent d0779ad58a
commit 7f75ec235f
5 changed files with 3154 additions and 601 deletions

2303
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,8 @@
#ifndef BOOT_IMG_HDR_H
#define BOOT_IMG_HDR_H
#include <stdint.h>
/*
** +-----------------+
** | boot header | 1 page
@@ -54,27 +56,27 @@
struct boot_img_hdr
{
unsigned char magic[BOOT_MAGIC_SIZE];
uint8_t magic[BOOT_MAGIC_SIZE];
unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */
uint32_t kernel_size; /* size in bytes */
uint32_t kernel_addr; /* physical load addr */
unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */
uint32_t ramdisk_size; /* size in bytes */
uint32_t ramdisk_addr; /* physical load addr */
unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
unsigned tags_addr; /* physical addr for kernel tags */
unsigned page_size; /* flash page size we assume */
unsigned dt_size; /* device tree in bytes */
unsigned unused; /* future expansion: should be 0 */
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
uint32_t dt_size; /* device tree in bytes */
uint32_t unused; /* future expansion: should be 0 */
unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
unsigned char cmdline[BOOT_ARGS_SIZE];
uint8_t cmdline[BOOT_ARGS_SIZE];
unsigned id[8]; /* timestamp / checksum / sha1 / etc */
uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
};
typedef struct boot_img_hdr boot_img_hdr;

View File

@@ -1,3 +1,7 @@
/**
* @file
* @brief This file contains all public libbootimg methods, defines and structs
*/
#ifndef LIBBOOTIMG_H
#define LIBBOOTIMG_h
@@ -6,12 +10,16 @@ extern "C" {
#endif
#include <stdint.h>
#include <stdio.h>
#include "boot_img_hdr.h"
#define LIBBOOTIMG_VERSION 0x000112 // 0xMMNNPP
#define LIBBOOTIMG_VERSION_STR "0.1.12"
#define LIBBOOTIMG_VERSION 0x000200 // 0xMMNNPP
#define LIBBOOTIMG_VERSION_STR "0.2.0"
enum
/**
* Enum containing possible blob types in a boot image.
*/
enum libbootimg_blob_type
{
LIBBOOTIMG_BLOB_KERNEL = 0,
LIBBOOTIMG_BLOB_RAMDISK = 1,
@@ -21,66 +29,228 @@ enum
LIBBOOTIMG_BLOB_CNT
};
/**
* Enum with masks passed to libbootimg_init_load method.
* Specifies which parts of boot image to load from disk.
*/
enum libbootimg_blob_load_mask
{
LIBBOOTIMG_LOAD_HDR_ONLY = 0x00,
LIBBOOTIMG_LOAD_KERNEL = (1 << LIBBOOTIMG_BLOB_KERNEL),
LIBBOOTIMG_LOAD_RAMDISK = (1 << LIBBOOTIMG_BLOB_RAMDISK),
LIBBOOTIMG_LOAD_SECOND = (1 << LIBBOOTIMG_BLOB_SECOND),
LIBBOOTIMG_LOAD_DTB = (1 << LIBBOOTIMG_BLOB_DTB),
LIBBOOTIMG_LOAD_ALL = ( LIBBOOTIMG_LOAD_KERNEL |
LIBBOOTIMG_LOAD_RAMDISK |
LIBBOOTIMG_LOAD_SECOND |
LIBBOOTIMG_LOAD_DTB )
};
/**
* Enum with all possible error return values.
*/
// Keep libbootimg_error_str updated!
enum libbootimg_error
{
LIBBOOTIMG_SUCCESS = 0,
LIBBOOTIMG_ERROR_IO = -1,
LIBBOOTIMG_ERROR_ACCESS = -2,
LIBBOOTIMG_ERROR_NOT_FOUND = -3,
LIBBOOTIMG_ERROR_INVALID_MAGIC = -4,
LIBBOOTIMG_ERROR_IMG_EOF = -5,
LIBBOOTIMG_ERROR_NO_BLOB_DATA = -6,
LIBBOOTIMG_ERROR_FILE_TOO_BIG = -7,
LIBBOOTIMG_ERROR_MISSING_BLOB = -8,
LIBBOOTIMG_ERROR_INVALID_PAGESIZE = -9,
LIBBOOTIMG_ERROR_OTHER = -128
};
/**
* One data blob from boot image.
*/
struct bootimg_blob
{
uint8_t **data;
uint32_t *size;
uint8_t *data;
uint32_t *size; /*!< Pointer to size of this blob in struct boot_img_hdr. Never change the address this is pointing to! */
};
/**
* Main libbootimg struct with all data.
* You will use this struct to work with libbootimg.
*/
struct bootimg
{
struct boot_img_hdr hdr;
uint8_t *kernel;
uint8_t *ramdisk;
uint8_t *second;
uint8_t *dtb;
uint32_t size;
int size_is_max_only;
struct bootimg_blob *blobs;
};
// for libbootimg_load_parts
enum
{
LIBBOOTIMG_LOAD_ONLY_HDR = 0x00,
LIBBOOTIMG_LOAD_KERNEL = (1 << LIBBOOTIMG_BLOB_KERNEL),
LIBBOOTIMG_LOAD_RAMDISK = (1 << LIBBOOTIMG_BLOB_RAMDISK),
LIBBOOTIMG_LOAD_SECOND = (1 << LIBBOOTIMG_BLOB_SECOND),
LIBBOOTIMG_LOAD_DTB = (1 << LIBBOOTIMG_BLOB_DTB),
LIBBOOTIMG_LOAD_ALL = 0xFFFFFFFF
struct boot_img_hdr hdr; /*!< Boot image header */
struct bootimg_blob blobs[LIBBOOTIMG_BLOB_CNT]; /*!< Blobs packed in the boot image. */
};
/**
* Initializes the struct bootimg and leaves it empty with some default values.
* It fills in the magic and default pagesize in struct boot_img_hdr.
* @see struct boot_img_hdr
* @param img pointer to (uninitialized) struct bootimg
*/
void libbootimg_init_new(struct bootimg *img);
void libbootimg_init_blob_table(struct bootimg *img);
int libbootimg_init_load(struct bootimg *img, const char *path);
int libbootimg_init_load_parts(struct bootimg *img, const char *path, int load_blob_mask);
/**
* Initializes the struct bootimg and loads data into it.
* @param img pointer to (uninitialized) struct bootimg
* @param path path to boot.img to load data from
* @param load_blob_mask mask specifying which parts to load into the bootimg struct
* @see enum libbootimg_blob_load_mask
* @return Zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_init_load(struct bootimg *img, const char *path, int load_blob_mask);
/**
* Loads boot_img_hdr from file on disk
* @param hdr pointer to boot_img_hdr structure
* @param path path to boot.img to load header from
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_load_header(struct boot_img_hdr *hdr, const char *path);
/**
* Frees all resources used by this bootimg struct
* @param b pointer to struct bootimg
*/
void libbootimg_destroy(struct bootimg *b);
int libbootimg_dump_kernel(struct bootimg *b, const char *dest);
int libbootimg_dump_ramdisk(struct bootimg *b, const char *dest);
int libbootimg_dump_second(struct bootimg *b, const char *dest);
int libbootimg_dump_dtb(struct bootimg *b, const char *dest);
int libbootimg_dump(struct bootimg *b, const char *dest_dir);
/**
* Writes blob to a file.
* @param blob pointer to source struct bootimg_blob
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_dump_blob(struct bootimg_blob *blob, const char *dest);
/**
* Writes kernel blob to a file.
* @param b pointer to struct bootimg
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_dump_kernel(struct bootimg *b, const char *dest);
/**
* Writes ramdisk blob to a file.
* @param b pointer to struct bootimg
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_dump_ramdisk(struct bootimg *b, const char *dest);
/**
* Writes second stage blob to a file.
* @param b pointer to struct bootimg
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_dump_second(struct bootimg *b, const char *dest);
/**
* Writes DTB blob to a file.
* @param b pointer to struct bootimg
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_dump_dtb(struct bootimg *b, const char *dest);
/**
* Loads blob data from a file.
* @param blob pointer to dest struct bootimg_blob
* @param src path to source file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_load_blob(struct bootimg_blob *blob, const char *src);
/**
* Loads kernel blob data from a file.
* @param b pointer to struct bootimg
* @param src path to source file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_load_kernel(struct bootimg *b, const char *src);
/**
* Loads ramdisk blob data from a file.
* @param b pointer to struct bootimg
* @param src path to source file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_load_ramdisk(struct bootimg *b, const char *src);
/**
* Loads second stage blob data from a file.
* @param b pointer to struct bootimg
* @param src path to source file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_load_second(struct bootimg *b, const char *src);
/**
* Loads DTB blob data from a file.
* @param b pointer to struct bootimg
* @param src path to source file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_load_dtb(struct bootimg *b, const char *src);
/**
* Writes boot image to a file
* @param b pointer to struct bootimg
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_write_img(struct bootimg *b, const char *dest);
/**
* Writes boot image to a file
* @param b pointer to struct bootimg
* @param f pointer to FILE to write data into
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_write_img_fileptr(struct bootimg *b, FILE *f);
/**
* Writes boot image to a file and then calls libbootimg_destroy.
* The bootimg struct is destroyed even if this function fails.
* @param b pointer to struct bootimg
* @param dest path to destination file
* @return zero if successful, negative value from libbootimg_error if failed.
*/
int libbootimg_write_img_and_destroy(struct bootimg *b, const char *dest);
int libbootimg_load_config(struct bootimg *b, const char *src, int *error_line);
int libbootimg_load_config_line(struct bootimg *b, char *line);
int libbootimg_write_config(struct bootimg *b, const char *dst);
/**
* Returns version number, format is 0xMMNNPP, so for version 0.1.12 it would return 0x000112
* @return version number in 0xMMNNPP format
*/
uint32_t libbootimg_version(void);
/**
* Returns version string
* @return version string, e.g. 0.1.12
*/
const char *libbootimg_version_str(void);
/**
* Translates value from enum libbootimg_error to readable string.
* @param error value from libbootimg_error
* @see enum libbootimg_error
* @return readable error string
*/
const char *libbootimg_error_str(int error);
#ifdef __cplusplus
}
#endif

View File

@@ -2,6 +2,10 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../include/libbootimg.h"
@@ -10,16 +14,30 @@
#define ACT_UPDATE 2
#define ACT_CREATE 3
static const char *default_fname_cfg = "bootimg.cfg";
static const char *default_fname_blobs[] = {
"zImage", // LIBBOOTIMG_BLOB_KERNEL
"initrd.img", // LIBBOOTIMG_BLOB_RAMDISK
"stage2.img", // LIBBOOTIMG_BLOB_SECOND
"dtb.img", // LIBBOOTIMG_BLOB_DTB
};
static const char *blob_names[] = {
"kernel", // LIBBOOTIMG_BLOB_KERNEL
"ramdisk", // LIBBOOTIMG_BLOB_RAMDISK
"second stage", // LIBBOOTIMG_BLOB_SECOND
"DTB", // LIBBOOTIMG_BLOB_DTB
};
struct bbootimg_info
{
struct bootimg img;
off_t img_size;
int size_is_max_only;
int act;
char *fname_img;
char *fname_kernel;
char *fname_ramdisk;
char *fname_second;
char *fname_dtb;
char *fname_cfg;
const char *fname_img;
const char *fname_cfg;
const char *fname_blobs[LIBBOOTIMG_BLOB_CNT];
};
static void print_help(const char *prog_name)
@@ -35,7 +53,7 @@ static void print_help(const char *prog_name)
"\n"
"%s -j <bootimg> - print image information in JSON\n"
"\n"
"%s -x <bootimg> [<bootimg.cfg> [<kernel> [<ramdisk> [<secondstage>]]]]\n"
"%s -x <bootimg> [<bootimg.cfg> [<kernel> [<ramdisk> [<secondstage> [<dtb>]]]]]\n"
" extract objects from boot image:\n"
" - config file (bootimg.cfg)\n"
" - kernel image (zImage)\n"
@@ -69,17 +87,200 @@ static void print_help(const char *prog_name)
,libbootimg_version_str(), prog_name, prog_name, prog_name, prog_name, prog_name, prog_name);
}
static int print_info(const char *path)
static int write_config(struct bbootimg_info *i, const char *dst)
{
struct bootimg img;
int res = libbootimg_init_load_parts(&img, path, LIBBOOTIMG_LOAD_ONLY_HDR);
int res;
FILE *f;
f = fopen(dst, "w");
if(!f)
return -errno;
res = fprintf(f,
"bootsize = 0x%X\n"
"pagesize = 0x%X\n"
"kerneladdr = 0x%X\n"
"ramdiskaddr = 0x%X\n"
"secondaddr = 0x%X\n"
"tagsaddr = 0x%X\n"
"name = %s\n"
"cmdline = %s\n",
(uint32_t)i->img_size, i->img.hdr.page_size, i->img.hdr.kernel_addr, i->img.hdr.ramdisk_addr,
i->img.hdr.second_addr, i->img.hdr.tags_addr, i->img.hdr.name, i->img.hdr.cmdline);
fclose(f);
return res;
}
static void parse_config_str(char *dest, const char *arg_start, const char *arg_end, size_t maxlen)
{
size_t len = 0;
if(*arg_start)
{
maxlen -= 1; // it includes the NULL
len = arg_end - arg_start;
if(len > maxlen)
len = maxlen;
strncpy(dest, arg_start, len);
}
dest[len] = 0;
}
static int load_config_line(struct bbootimg_info *i, const char *line)
{
const char *start, *end;
char *name_e;
char *arg_s;
size_t n_to_cmp, len;
for(start = line; isspace(*start); ++start);
for(end = start+strlen(start); isspace(*(end-1)); --end);
if(*start == 0 || (name_e = strchr(start, '=')) == NULL)
return 0;
arg_s = name_e+1;
for(; isspace(*(name_e-1)) && name_e > start; --name_e);
for(; isspace(*arg_s); ++arg_s);
n_to_cmp = name_e - start;
if(strncmp("bootsize", start, n_to_cmp) == 0)
i->img_size = strtoll(arg_s, NULL, 0);
else if(strncmp("pagesize", start, n_to_cmp) == 0)
i->img.hdr.page_size = strtoll(arg_s, NULL, 0);
else if(strncmp("kerneladdr", start, n_to_cmp) == 0)
i->img.hdr.kernel_addr = strtoll(arg_s, NULL, 0);
else if(strncmp("ramdiskaddr", start, n_to_cmp) == 0)
i->img.hdr.ramdisk_addr = strtoll(arg_s, NULL, 0);
else if(strncmp("secondaddr", start, n_to_cmp) == 0)
i->img.hdr.second_addr = strtoll(arg_s, NULL, 0);
else if(strncmp("tagsaddr", start, n_to_cmp) == 0)
i->img.hdr.tags_addr = strtoll(arg_s, NULL, 0);
else if(strncmp("name", start, n_to_cmp) == 0)
parse_config_str((char*)i->img.hdr.name, arg_s, end, BOOT_NAME_SIZE);
else if(strncmp("cmdline", start, n_to_cmp) == 0)
parse_config_str((char*)i->img.hdr.cmdline, arg_s, end, BOOT_ARGS_SIZE);
else
return -1;
return 0;
}
static int load_config(struct bbootimg_info *i, const char *src, int *error_line)
{
FILE *f;
int res = 0;
int line_num = 0;
char line[1024];
f = fopen(src, "r");
if(!f)
return -errno;
while(fgets(line, sizeof(line), f))
{
if(load_config_line(i, line) < 0)
{
res = -1;
if(error_line)
*error_line = line_num;
goto exit;
}
++line_num;
}
exit:
fclose(f);
return res;
}
static int load_bootimg(struct bootimg *b, const char *path)
{
int res = libbootimg_init_load(b, path, LIBBOOTIMG_LOAD_ALL);
if(res < 0)
{
fprintf(stderr, "Failed to load bootimg \"%s\" (%s)!\n", path, strerror(-res));
fprintf(stderr, "Failed to load boot image (%s)!", libbootimg_error_str(res));
return -res;
}
return 0;
}
static off_t get_bootimg_size(const char *path)
{
struct stat info;
if(stat(path, &info) < 0)
{
fprintf(stderr, "Failed to get boot image size (%s)!", strerror(errno));
return -1;
}
// If this is a block device, stat will report zero size.
// abootimg uses libblkid to get the correct size, but
// I don't wanna drag in any dependencies.
if(info.st_size == 0)
{
off_t size;
FILE *f;
f = fopen(path, "r");
if(!f)
{
fprintf(stderr, "Failed to get boot image size (%s)!", strerror(errno));
return -1;
}
fseek(f, 0, SEEK_END);
size = ftell(f);
fclose(f);
if(size <= 0)
{
fprintf(stderr, "Failed to get boot image size (fseek -> ftell failed, %s)!", strerror(errno));
return -1;
}
return size;
}
else
{
return info.st_size;
}
}
static void load_default_filenames(struct bbootimg_info *i)
{
int x;
i->fname_cfg = default_fname_cfg;
for(x = 0; x < LIBBOOTIMG_BLOB_CNT; ++x)
i->fname_blobs[x] = default_fname_blobs[x];
}
static int print_info(const char *path)
{
int i;
int res;
off_t size;
struct bootimg img;
char name[BOOT_NAME_SIZE+1];
res = libbootimg_init_load(&img, path, LIBBOOTIMG_LOAD_HDR_ONLY);
if(res < 0)
{
fprintf(stderr, "Failed to load bootimg \"%s\" (%s)!\n", path, libbootimg_error_str(res));
return 1;
}
char name[BOOT_NAME_SIZE+1];
if((size = get_bootimg_size(path)) < 0)
return 1;
snprintf(name, sizeof(name), "%s", img.hdr.name);
img.hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
@@ -87,7 +288,7 @@ static int print_info(const char *path)
printf ("\nAndroid Boot Image Info:\n\n");
printf ("* file name = %s\n\n", path);
printf ("* image size = %u bytes (%.2f MB)\n", img.size, (double)img.size/0x100000);
printf ("* image size = %ld bytes (%.2f MB)\n", size, (double)size/0x100000);
printf (" page size = %u bytes\n\n", img.hdr.page_size);
printf ("* Boot Name = \"%s\"\n\n", name);
@@ -112,8 +313,7 @@ static int print_info(const char *path)
printf ("* empty cmdline\n");
printf ("* id = ");
int i;
for (i=0; i<8; i++)
for (i = 0; i < 8; ++i)
printf ("0x%08x ", img.hdr.id[i]);
printf ("\n\n");
@@ -123,23 +323,29 @@ static int print_info(const char *path)
static int print_json(const char *path)
{
int i;
int res;
off_t size;
struct bootimg img;
int res = libbootimg_init_load_parts(&img, path, LIBBOOTIMG_LOAD_ONLY_HDR);
char name[BOOT_NAME_SIZE+1];
res = libbootimg_init_load(&img, path, LIBBOOTIMG_LOAD_HDR_ONLY);
if(res < 0)
{
fprintf(stderr, "Failed to load bootimg \"%s\" (%s)!\n", path, strerror(-res));
fprintf(stderr, "Failed to load bootimg \"%s\" (%s)!\n", path, libbootimg_error_str(res));
return 1;
}
int i;
char name[BOOT_NAME_SIZE+1];
if((size = get_bootimg_size(path)) < 0)
return 1;
snprintf(name, sizeof(name), "%s", img.hdr.name);
img.hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
printf("{\n");
printf(" \"bbootimg_version\": %u,\n", libbootimg_version());
printf(" \"img_size\": %u,\n", img.size);
printf(" \"img_size\": %ld,\n", size);
printf(" \"boot_img_hdr\": {\n");
printf(" \"kernel_size\": %u,\n", img.hdr.kernel_size);
printf(" \"kernel_addr\": %u,\n", img.hdr.kernel_addr);
@@ -165,61 +371,41 @@ static int print_json(const char *path)
static int extract_bootimg(struct bbootimg_info *i)
{
int res = libbootimg_init_load(&i->img, i->fname_img);
int x;
int res;
struct bootimg_blob *blob;
res = libbootimg_init_load(&i->img, i->fname_img, LIBBOOTIMG_LOAD_ALL);
if(res < 0)
{
fprintf(stderr, "Failed to load boot image (%s)!\n", strerror(-res));
fprintf(stderr, "Failed to load boot image (%s)!\n", libbootimg_error_str(res));
return -res;
}
const char *cfg = i->fname_cfg ? i->fname_cfg : "bootimg.cfg";
printf("writing boot image config in %s\n", cfg);
res = libbootimg_write_config(&i->img, cfg);
if((i->img_size = get_bootimg_size(i->fname_img)) < 0)
return -1;
printf("writing boot image config in %s\n", i->fname_cfg);
res = write_config(i, i->fname_cfg);
if(res < 0)
{
fprintf(stderr, "Failed to write bootimg cfg (%s)!\n", strerror(-res));
return -res;
}
const char *kernel = i->fname_kernel ? i->fname_kernel : "zImage";
printf("extracting kernel in %s\n", kernel);
res = libbootimg_dump_kernel(&i->img, kernel);
if(res < 0)
for(x = 0; x < LIBBOOTIMG_BLOB_CNT; ++x)
{
fprintf(stderr, "Failed to extract kernel (%s)!\n", strerror(-res));
return -res;
}
blob = &i->img.blobs[x];
const char *ramdisk = i->fname_ramdisk ? i->fname_ramdisk : "initrd.img";
printf("extracting ramdisk in %s\n", ramdisk);
res = libbootimg_dump_ramdisk(&i->img, ramdisk);
if(res < 0)
{
fprintf(stderr, "Failed to extract ramdisk (%s)!\n", strerror(-res));
return -res;
}
if(*blob->size == 0)
continue;
if(i->img.hdr.second_size > 0)
{
const char *second = i->fname_second ? i->fname_second : "stage2.img";
printf("extracting second stage in %s\n", second);
res = libbootimg_dump_second(&i->img, second);
printf("extracting %s in %s\n", blob_names[x], i->fname_blobs[x]);
res = libbootimg_dump_blob(blob, i->fname_blobs[x]);
if(res < 0)
{
fprintf(stderr, "Failed to extract second stage (%s)!\n", strerror(-res));
return -res;
}
}
if(i->img.hdr.dt_size > 0)
{
const char *dtb = i->fname_dtb ? i->fname_dtb : "dtb.img";
printf("extracting DTB in %s\n", dtb);
res = libbootimg_dump_dtb(&i->img, dtb);
if(res < 0)
{
fprintf(stderr, "Failed to extract DTB (%s)!\n", strerror(-res));
return -res;
fprintf(stderr, "Failed to extract %s (%s)!\n", blob_names[x], libbootimg_error_str(res));
return res;
}
}
@@ -227,33 +413,28 @@ static int extract_bootimg(struct bbootimg_info *i)
return 0;
}
static int copy_file(const char *src, const char *dst)
static int copy_file(FILE *in, const char *dst)
{
FILE *in = fopen(src, "r");
if(!in)
{
fprintf(stderr, "Failed to open src file!\n");
return -1;
}
#define CPY_BUFF_SIZE (512*1024)
FILE *out;
int res = -1;
size_t read;
char *buff = NULL;
FILE *out = fopen(dst, "w");
out = fopen(dst, "w");
if(!out)
{
fclose(in);
fprintf(stderr, "Failed to open dst file!\n");
fprintf(stderr, "Failed to open dst file (%s)!\n", strerror(errno));
return -1;
}
int res = -1;
#define BUFF_SIZE (512*1024)
char *buff = malloc(BUFF_SIZE);
size_t read;
buff = malloc(CPY_BUFF_SIZE);
while((read = fread(buff, 1, BUFF_SIZE, in)) > 0)
while((read = fread(buff, 1, CPY_BUFF_SIZE, in)) > 0)
{
if(fwrite(buff, 1, read, out) != read)
{
fprintf(stderr, "Failed to write data to dst file!\n");
fprintf(stderr, "Failed to write data to dst file (%s)!\n", strerror(errno));
goto exit;
}
}
@@ -261,80 +442,69 @@ static int copy_file(const char *src, const char *dst)
res = 0;
exit:
free(buff);
fclose(in);
fclose(out);
return res;
}
static int update_bootimg(struct bbootimg_info *i)
{
int x;
int res = -1;
char *tmpname = malloc(strlen(i->fname_img)+sizeof(".new"));
strcpy(tmpname, i->fname_img);
strcat(tmpname, ".new");
FILE *tmp = NULL;
struct bootimg_blob *blob;
if(i->fname_kernel)
for(x = 0; x < LIBBOOTIMG_BLOB_CNT; ++x)
{
printf("reading kernel from %s\n", i->fname_kernel);
res = libbootimg_load_kernel(&i->img, i->fname_kernel);
if(!i->fname_blobs[x])
continue;
blob = &i->img.blobs[x];
printf("reading %s from %s\n", blob_names[x], i->fname_blobs[x]);
res = libbootimg_load_blob(blob, i->fname_blobs[x]);
if(res < 0)
{
res = -res;
fprintf(stderr, "Failed to load kernel (%s)!\n", strerror(res));
goto exit;
}
}
if(i->fname_ramdisk)
{
printf("reading ramdisk from %s\n", i->fname_ramdisk);
res = libbootimg_load_ramdisk(&i->img, i->fname_ramdisk);
if(res < 0)
{
res = -res;
fprintf(stderr, "Failed to load ramdisk (%s)!\n", strerror(res));
goto exit;
}
}
if(i->fname_second)
{
printf("reading second stage from %s\n", i->fname_second);
res = libbootimg_load_second(&i->img, i->fname_second);
if(res < 0)
{
res = -res;
fprintf(stderr, "Failed to load second stage (%s)!\n", strerror(res));
goto exit;
}
}
if(i->fname_dtb)
{
printf("reading device tree blob from %s\n", i->fname_dtb);
res = libbootimg_load_dtb(&i->img, i->fname_dtb);
if(res < 0)
{
res = -res;
fprintf(stderr, "Failed to load device tree blob (%s)!\n", strerror(res));
fprintf(stderr, "Failed to load %s (%s)!\n", blob_names[x], libbootimg_error_str(res));
goto exit;
}
}
printf("Writing Boot Image %s\n", i->fname_img);
res = libbootimg_write_img(&i->img, tmpname);
if(res < 0)
tmp = tmpfile();
if(!tmp)
{
res = -res;
fprintf(stderr, "Failed to create boot image (%s)!\n", strerror(res));
fprintf(stderr, "Failed to create tmp file (%s)!\n", strerror(errno));
goto exit;
}
copy_file(tmpname, i->fname_img);
res = libbootimg_write_img_fileptr(&i->img, tmp);
if(res < 0)
{
fprintf(stderr, "Failed to create boot image (%s)!\n", libbootimg_error_str(res));
goto exit;
}
// bootimg size (abootimg compatibility)
if(i->img_size != 0)
{
if(i->img_size < ftell(tmp))
{
fprintf(stderr, "Failed to create boot image: the result image is too big\n");
res = -1;
goto exit;
}
if(i->size_is_max_only == 0)
ftruncate(fileno(tmp), i->img_size);
}
rewind(tmp);
copy_file(tmp, i->fname_img);
exit:
remove(tmpname);
free(tmpname);
if(tmp)
fclose(tmp);
libbootimg_destroy(&i->img);
return res;
}
@@ -349,7 +519,7 @@ static int execute_action(struct bbootimg_info *i)
return update_bootimg(i);
case ACT_CREATE:
{
if(!i->fname_kernel || !i->fname_ramdisk)
if(!i->fname_blobs[LIBBOOTIMG_BLOB_KERNEL] || !i->fname_blobs[LIBBOOTIMG_BLOB_RAMDISK])
{
fprintf(stderr, "You have to specify both ramdisk and kernel to create boot image!\n");
return EINVAL;
@@ -360,18 +530,7 @@ static int execute_action(struct bbootimg_info *i)
return EINVAL;
}
static int load_bootimg(struct bootimg *b, const char *path)
{
int res = libbootimg_init_load(b, path);
if(res < 0)
{
fprintf(stderr, "Failed to load boot image (%s)!", strerror(-res));
return -res;
}
return 0;
}
int main(int argc, char *argv[])
int main(int argc, const char *argv[])
{
int i;
@@ -389,7 +548,7 @@ int main(int argc, char *argv[])
}
if(strcmp("-m", argv[i]) == 0)
info.img.size_is_max_only = 1;
info.size_is_max_only = 1;
if(i+1 >= argc)
continue;
@@ -402,27 +561,31 @@ int main(int argc, char *argv[])
// actions
if(strcmp("-x", argv[i]) == 0)
{
int blob_itr;
load_default_filenames(&info);
info.act = ACT_EXTRACT;
info.fname_img = argv[++i];
if(++i < argc)
info.fname_cfg = argv[i];
if(++i < argc)
info.fname_kernel = argv[i];
if(++i < argc)
info.fname_ramdisk = argv[i];
if(++i < argc)
info.fname_second = argv[i];
if(++i < argc)
info.fname_dtb = argv[i];
for(blob_itr = 0; ++i < argc && blob_itr < LIBBOOTIMG_BLOB_CNT; ++blob_itr)
info.fname_blobs[blob_itr] = argv[i];
break;
}
else if(strcmp("-u", argv[i]) == 0)
{
info.act = ACT_UPDATE;
info.fname_img = argv[++i];
if(load_bootimg(&info.img, info.fname_img) != 0)
if (load_bootimg(&info.img, info.fname_img) != 0)
return -1;
if((info.img_size = get_bootimg_size(info.fname_img)) < 0)
return -1;
}
else if(strcmp("--create", argv[i]) == 0)
{
@@ -432,7 +595,7 @@ int main(int argc, char *argv[])
// params
else if(strcmp("-c", argv[i]) == 0)
{
if(libbootimg_load_config_line(&info.img, argv[++i]) < 0)
if(load_config_line(&info, argv[++i]) < 0)
{
fprintf(stderr, "Invalid config option \"%s\"\n\n", argv[i]);
goto exit_help;
@@ -444,7 +607,7 @@ int main(int argc, char *argv[])
printf("reading config file %s\n", info.fname_cfg);
int line = -1;
int res = libbootimg_load_config(&info.img, info.fname_cfg, &line);
int res = load_config(&info, info.fname_cfg, &line);
if(res < 0)
{
res = -res;
@@ -453,13 +616,13 @@ int main(int argc, char *argv[])
}
}
else if(strcmp("-k", argv[i]) == 0)
info.fname_kernel = argv[++i];
info.fname_blobs[LIBBOOTIMG_BLOB_KERNEL] = argv[++i];
else if(strcmp("-r", argv[i]) == 0)
info.fname_ramdisk = argv[++i];
info.fname_blobs[LIBBOOTIMG_BLOB_RAMDISK] = argv[++i];
else if(strcmp("-s", argv[i]) == 0)
info.fname_second = argv[++i];
info.fname_blobs[LIBBOOTIMG_BLOB_SECOND] = argv[++i];
else if(strcmp("-d", argv[i]) == 0)
info.fname_dtb = argv[++i];
info.fname_blobs[LIBBOOTIMG_BLOB_DTB] = argv[++i];
else
{
fprintf(stderr, "Unknown argument: %s\n\n", argv[i]);
@@ -468,7 +631,7 @@ int main(int argc, char *argv[])
}
if(info.act != ACT_HELP)
return execute_action(&info);
return execute_action(&info) == 0 ? 0 : 1;
exit_help:
print_help(argv[0]);

View File

@@ -6,286 +6,44 @@
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <assert.h>
#include "../include/libbootimg.h"
#define DEFAULT_PAGE_SIZE 2048
#define MAX_PATH_LEN 4096
static inline unsigned align_size(unsigned size, unsigned page_size)
{
return ((size + page_size - 1)/page_size)*page_size;
}
void libbootimg_init_new(struct bootimg *img)
static int translate_errnum(int errnum)
{
memset(img, 0, sizeof(struct bootimg));
memcpy(img->hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
img->hdr.page_size = DEFAULT_PAGE_SIZE;
}
void libbootimg_init_blob_table(struct bootimg *img)
{
if(img->blobs)
return;
img->blobs = calloc(LIBBOOTIMG_BLOB_CNT, sizeof(struct bootimg_blob));
#define ADD_BLOB(i, d, s) \
img->blobs[i].data = &d; \
img->blobs[i].size = &s;
ADD_BLOB(LIBBOOTIMG_BLOB_KERNEL, img->kernel, img->hdr.kernel_size)
ADD_BLOB(LIBBOOTIMG_BLOB_RAMDISK, img->ramdisk, img->hdr.ramdisk_size)
ADD_BLOB(LIBBOOTIMG_BLOB_SECOND, img->second, img->hdr.second_size)
ADD_BLOB(LIBBOOTIMG_BLOB_DTB, img->dtb, img->hdr.dt_size)
}
int libbootimg_init_load(struct bootimg *img, const char *path)
{
return libbootimg_init_load_parts(img, path, LIBBOOTIMG_LOAD_ALL);
}
int libbootimg_init_load_parts(struct bootimg *img, const char *path, int load_blob_mask)
{
int i;
FILE *f;
int res = 0;
long long addr;
libbootimg_init_new(img);
res = libbootimg_load_header(&img->hdr, path);
if(res < 0)
switch(errno)
{
libbootimg_destroy(img);
return res;
case EIO: return LIBBOOTIMG_ERROR_IO;
case EACCES: return LIBBOOTIMG_ERROR_ACCESS;
case ENOENT: return LIBBOOTIMG_ERROR_NOT_FOUND;
default: return LIBBOOTIMG_ERROR_OTHER;
}
libbootimg_init_blob_table(img);
f = fopen(path, "r");
if(!f)
return -errno;
addr = img->hdr.page_size;
for(i = 0; i < LIBBOOTIMG_BLOB_CNT; ++i)
{
if((load_blob_mask & (1 << i)) && *img->blobs[i].size != 0)
{
*img->blobs[i].data = malloc(*img->blobs[i].size);
if(fseek(f, addr, SEEK_SET) < 0)
goto fail_errno;
if(fread(*img->blobs[i].data, *img->blobs[i].size, 1, f) != 1)
{
if(feof(f))
{
res = -EINVAL;
goto fail;
}
else // IO error
{
res = -EIO;
goto fail;
}
}
}
addr += align_size(*img->blobs[i].size, img->hdr.page_size);
}
fseek(f, 0, SEEK_END);
img->size = ftell(f);
fclose(f);
return 0;
fail_errno:
res = -errno;
fail:
libbootimg_destroy(img);
fclose(f);
return res;
}
void libbootimg_destroy(struct bootimg *b)
static int translate_fread_error(FILE *f)
{
free(b->kernel);
free(b->ramdisk);
free(b->second);
free(b->dtb);
b->kernel = b->ramdisk = b->second = b->dtb = NULL;
free(b->blobs);
b->blobs = NULL;
}
int libbootimg_load_header(struct boot_img_hdr *hdr, const char *path)
{
FILE *f = fopen(path, "r");
if(!f)
return -errno;
int res = fread(hdr, sizeof(struct boot_img_hdr), 1, f);
if(res == 1)
{
if(memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0)
res = 0;
else
res = -EIO;
}
if(ferror(f))
return LIBBOOTIMG_ERROR_IO;
else if(feof(f))
return LIBBOOTIMG_ERROR_IMG_EOF;
else
res = errno ? -errno : -EIO;
fclose(f);
return res;
}
static int dump_part(uint8_t *data, unsigned len, const char *dest)
{
int res = 0;
FILE *f;
if(data == NULL)
return -EINVAL;
f = fopen(dest, "w");
if(!f)
return -errno;
if(fwrite(data, len, 1, f) != 1)
res = -errno;
fclose(f);
return res;
}
int libbootimg_dump_kernel(struct bootimg *b, const char *dest)
{
return dump_part(b->kernel, b->hdr.kernel_size, dest);
}
int libbootimg_dump_ramdisk(struct bootimg *b, const char *dest)
{
return dump_part(b->ramdisk, b->hdr.ramdisk_size, dest);
}
int libbootimg_dump_second(struct bootimg *b, const char *dest)
{
if(b->hdr.second_size == 0)
return -ENOENT;
return dump_part(b->second, b->hdr.second_size, dest);
}
int libbootimg_dump_dtb(struct bootimg *b, const char *dest)
{
if(b->hdr.dt_size == 0)
return -ENOENT;
return dump_part(b->dtb, b->hdr.dt_size, dest);
}
int libbootimg_dump(struct bootimg *b, const char *dest_dir)
{
char dest[MAX_PATH_LEN];
int res = 0;
snprintf(dest, sizeof(dest), "%s/zImage", dest_dir);
res = libbootimg_dump_kernel(b, dest);
if(res == 0)
{
snprintf(dest, sizeof(dest), "%s/initrd.img", dest_dir);
res = libbootimg_dump_ramdisk(b, dest);
}
if(res == 0 && b->hdr.second_size)
{
snprintf(dest, sizeof(dest), "%s/second.img", dest_dir);
res = libbootimg_dump_second(b, dest);
}
if(res == 0 && b->hdr.dt_size)
{
snprintf(dest, sizeof(dest), "%s/dtb.img", dest_dir);
res = libbootimg_dump_dtb(b, dest);
}
return res;
}
static int load_part(uint8_t **data, const char *src)
{
struct stat info;
if(stat(src, &info) < 0)
return -errno;
if(info.st_size > INT_MAX)
return -EFBIG;
// probably /dev/null
if(info.st_size == 0)
return 0;
FILE *f = fopen(src, "r");
if(!f)
return -errno;
int res = info.st_size;
*data = realloc(*data, info.st_size);
if(fread(*data, info.st_size, 1, f) != 1)
{
res = -EIO;
free(*data);
*data = NULL;
}
fclose(f);
return res;
}
int libbootimg_load_kernel(struct bootimg *b, const char *src)
{
int res = load_part(&b->kernel, src);
if(res >= 0)
b->hdr.kernel_size = res;
return res;
}
int libbootimg_load_ramdisk(struct bootimg *b, const char *src)
{
int res = load_part(&b->ramdisk, src);
if(res >= 0)
b->hdr.ramdisk_size = res;
return res;
}
int libbootimg_load_second(struct bootimg *b, const char *src)
{
int res = load_part(&b->second, src);
if(res >= 0)
b->hdr.second_size = res;
return res;
}
int libbootimg_load_dtb(struct bootimg *b, const char *src)
{
int res = load_part(&b->dtb, src);
if(res >= 0)
b->hdr.dt_size = res;
return res;
return LIBBOOTIMG_ERROR_OTHER;
}
// 32bit FNV-1a hash algorithm
// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
static uint32_t calc_fnv_hash(void *data, unsigned len)
{
static const uint32_t FNV_prime = 16777619U;
static const uint32_t offset_basis = 2166136261U;
const uint32_t FNV_prime = 16777619U;
const uint32_t offset_basis = 2166136261U;
uint32_t *d = (uint32_t*)data;
uint32_t i, max;
@@ -311,30 +69,269 @@ static uint32_t calc_fnv_hash(void *data, unsigned len)
static void fill_id_hashes(struct bootimg *b)
{
b->hdr.id[0] = calc_fnv_hash(b->kernel, b->hdr.kernel_size);
b->hdr.id[1] = calc_fnv_hash(b->ramdisk, b->hdr.ramdisk_size);
b->hdr.id[2] = calc_fnv_hash(b->second, b->hdr.second_size);
int i = 0;
b->hdr.id[3] = calc_fnv_hash(&b->hdr.kernel_addr, sizeof(b->hdr.kernel_addr));
b->hdr.id[4] = calc_fnv_hash(&b->hdr.ramdisk_addr, sizeof(b->hdr.ramdisk_addr));
b->hdr.id[5] = calc_fnv_hash(&b->hdr.second_addr, sizeof(b->hdr.second_addr));
// hash blobs
for(; i < LIBBOOTIMG_BLOB_CNT && i < 5; ++i)
b->hdr.id[i] = calc_fnv_hash(b->blobs[i].data, *b->blobs[i].size);
// hash kernel, ramdisk and second _addr and _size together
b->hdr.id[i++] = calc_fnv_hash(&b->hdr.kernel_size, sizeof(uint32_t)*6);
// hash tags_addr, page_size, dt_size and unused together
b->hdr.id[6] = calc_fnv_hash(&b->hdr.tags_addr, sizeof(b->hdr.tags_addr)*4);
b->hdr.id[i++] = calc_fnv_hash(&b->hdr.tags_addr, sizeof(uint32_t)*4);
// cmdline is directly after name, so hash them together
b->hdr.id[7] = calc_fnv_hash(b->hdr.name, BOOT_NAME_SIZE + strlen((char*)b->hdr.cmdline));
b->hdr.id[i++] = calc_fnv_hash(b->hdr.name, BOOT_NAME_SIZE + strlen((char*)b->hdr.cmdline));
}
void libbootimg_init_new(struct bootimg *img)
{
memset(img, 0, sizeof(struct bootimg));
memcpy(img->hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
img->hdr.page_size = DEFAULT_PAGE_SIZE;
img->blobs[LIBBOOTIMG_BLOB_KERNEL].size = &img->hdr.kernel_size;
img->blobs[LIBBOOTIMG_BLOB_RAMDISK].size = &img->hdr.ramdisk_size;
img->blobs[LIBBOOTIMG_BLOB_SECOND].size = &img->hdr.second_size;
img->blobs[LIBBOOTIMG_BLOB_DTB].size = &img->hdr.dt_size;
}
int libbootimg_init_load(struct bootimg *img, const char *path, int load_blob_mask)
{
int i;
FILE *f;
int res = 0;
int64_t addr;
struct bootimg_blob *blob;
libbootimg_init_new(img);
res = libbootimg_load_header(&img->hdr, path);
if(res < 0)
{
libbootimg_destroy(img);
return res;
}
f = fopen(path, "r");
if(!f)
return translate_errnum(errno);
addr = img->hdr.page_size;
for(i = 0; i < LIBBOOTIMG_BLOB_CNT; ++i)
{
blob = &img->blobs[i];
if((load_blob_mask & (1 << i)) && *blob->size != 0)
{
if(fseek(f, addr, SEEK_SET) < 0)
{
if(errno == EINVAL)
res = LIBBOOTIMG_ERROR_IMG_EOF;
else
res = translate_errnum(errno);
goto fail;
}
blob->data = malloc(*blob->size);
if(fread(blob->data, *blob->size, 1, f) != 1)
{
res = translate_fread_error(f);
goto fail;
}
}
addr += align_size(*blob->size, img->hdr.page_size);
}
fclose(f);
return 0;
fail:
libbootimg_destroy(img);
fclose(f);
return res;
}
void libbootimg_destroy(struct bootimg *b)
{
struct bootimg_blob *blob = b->blobs;
struct bootimg_blob * const blobs_end = blob + LIBBOOTIMG_BLOB_CNT;
for(; blob != blobs_end; ++blob)
{
free(blob->data);
blob->data = NULL;
}
}
int libbootimg_load_header(struct boot_img_hdr *hdr, const char *path)
{
int res = 0;
FILE *f = fopen(path, "r");
if(!f)
return translate_errnum(errno);
if(fread(hdr, sizeof(struct boot_img_hdr), 1, f) == 1)
{
if(memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0)
res = LIBBOOTIMG_ERROR_INVALID_MAGIC;
}
else
{
res = translate_fread_error(f);
}
fclose(f);
return res;
}
int libbootimg_dump_blob(struct bootimg_blob *blob, const char *dest)
{
FILE *f;
int res = 0;
if(blob->data == NULL)
return LIBBOOTIMG_ERROR_NO_BLOB_DATA;
f = fopen(dest, "w");
if(!f)
return translate_errnum(errno);
if(fwrite(blob->data, *blob->size, 1, f) != 1)
res = LIBBOOTIMG_ERROR_IO;
fclose(f);
return res;
}
int libbootimg_dump_kernel(struct bootimg *b, const char *dest)
{
return libbootimg_dump_blob(&b->blobs[LIBBOOTIMG_BLOB_KERNEL], dest);
}
int libbootimg_dump_ramdisk(struct bootimg *b, const char *dest)
{
return libbootimg_dump_blob(&b->blobs[LIBBOOTIMG_BLOB_RAMDISK], dest);
}
int libbootimg_dump_second(struct bootimg *b, const char *dest)
{
return libbootimg_dump_blob(&b->blobs[LIBBOOTIMG_BLOB_SECOND], dest);
}
int libbootimg_dump_dtb(struct bootimg *b, const char *dest)
{
return libbootimg_dump_blob(&b->blobs[LIBBOOTIMG_BLOB_DTB], dest);
}
int libbootimg_load_blob(struct bootimg_blob *blob, const char *src)
{
FILE *f;
int res = 0;
uint8_t *data;
struct stat info;
if(stat(src, &info) < 0)
return translate_errnum(errno);
if(info.st_size > INT_MAX)
return LIBBOOTIMG_ERROR_FILE_TOO_BIG;
// probably /dev/null
if(info.st_size == 0)
{
free(blob->data);
blob->data = NULL;
*blob->size = 0;
return 0;
}
f = fopen(src, "r");
if(!f)
return translate_errnum(errno);
data = malloc(info.st_size);
if(fread(data, info.st_size, 1, f) == 1)
{
free(blob->data);
blob->data = data;
*blob->size = info.st_size;
}
else
{
res = translate_fread_error(f);
free(data);
}
fclose(f);
return res;
}
int libbootimg_load_kernel(struct bootimg *b, const char *src)
{
return libbootimg_load_blob(&b->blobs[LIBBOOTIMG_BLOB_KERNEL], src);
}
int libbootimg_load_ramdisk(struct bootimg *b, const char *src)
{
return libbootimg_load_blob(&b->blobs[LIBBOOTIMG_BLOB_RAMDISK], src);
}
int libbootimg_load_second(struct bootimg *b, const char *src)
{
return libbootimg_load_blob(&b->blobs[LIBBOOTIMG_BLOB_SECOND], src);
}
int libbootimg_load_dtb(struct bootimg *b, const char *src)
{
return libbootimg_load_blob(&b->blobs[LIBBOOTIMG_BLOB_DTB], src);
}
int libbootimg_write_img(struct bootimg *b, const char *dest)
{
if(b->hdr.kernel_size == 0 || b->hdr.ramdisk_size == 0 || b->hdr.page_size < sizeof(b->hdr))
return -EINVAL;
FILE *f;
int res;
int res = 0;
FILE *f = fopen(dest, "w");
if(b->hdr.kernel_size == 0 || b->hdr.ramdisk_size == 0)
return LIBBOOTIMG_ERROR_MISSING_BLOB;
if(b->hdr.page_size < sizeof(b->hdr))
return LIBBOOTIMG_ERROR_INVALID_PAGESIZE;
f = fopen(dest, "w");
if(!f)
return -errno;
return translate_errnum(errno);
res = libbootimg_write_img_fileptr(b, f);
fclose(f);
return res;
}
int libbootimg_write_img_fileptr(struct bootimg *b, FILE *f)
{
int i;
int res = 0;
char *blank = NULL;
size_t padding;
struct bootimg_blob *blob;
int pos_start, pos_end;
pos_start = ftell(f);
if(pos_start < 0)
return translate_errnum(errno);
if(b->hdr.kernel_size == 0 || b->hdr.ramdisk_size == 0)
return LIBBOOTIMG_ERROR_MISSING_BLOB;
if(b->hdr.page_size < sizeof(b->hdr))
return LIBBOOTIMG_ERROR_INVALID_PAGESIZE;
// make sure it ends with 0
b->hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
@@ -345,54 +342,43 @@ int libbootimg_write_img(struct bootimg *b, const char *dest)
fill_id_hashes(b);
int i;
size_t to_write;
char *blank = malloc(b->hdr.page_size);
blank = malloc(b->hdr.page_size);
memset(blank, 0, b->hdr.page_size);
// write header
if(fwrite(&b->hdr, sizeof(b->hdr), 1, f) != 1)
goto fail;
goto fail_fwrite;
to_write = align_size(sizeof(b->hdr), b->hdr.page_size) - sizeof(b->hdr);
if(fwrite(blank, sizeof(char), to_write, f) != to_write)
goto fail;
libbootimg_init_blob_table(b);
padding = align_size(sizeof(b->hdr), b->hdr.page_size) - sizeof(b->hdr);
if(fwrite(blank, 1, padding, f) != padding)
goto fail_fwrite;
for(i = 0; i < LIBBOOTIMG_BLOB_CNT; ++i)
{
if(*b->blobs[i].size == 0)
blob = &b->blobs[i];
if(*blob->size == 0)
continue;
if(fwrite(*b->blobs[i].data, *b->blobs[i].size, 1, f) != 1)
goto fail;
if(fwrite(blob->data, *blob->size, 1, f) != 1)
goto fail_fwrite;
to_write = align_size(*b->blobs[i].size, b->hdr.page_size) - *b->blobs[i].size;
if(fwrite(blank, sizeof(char), to_write, f) != to_write)
goto fail;
padding = align_size(*blob->size, b->hdr.page_size) - *blob->size;
if(fwrite(blank, 1, padding, f) != padding)
goto fail_fwrite;
}
// bootimg size (abootimg compatibility)
if(b->size != 0)
{
if((int)b->size < ftell(f))
{
res = -EFBIG;
remove(dest);
goto exit;
}
pos_end = ftell(f);
if(b->size_is_max_only == 0)
ftruncate(fileno(f), b->size);
}
if(pos_end > 0)
res = pos_end - pos_start;
else
res = translate_errnum(errno);
goto exit;
fail:
res = -errno;
remove(dest);
fail_fwrite:
res = LIBBOOTIMG_ERROR_IO;
exit:
fclose(f);
free(blank);
return res;
}
@@ -404,97 +390,6 @@ int libbootimg_write_img_and_destroy(struct bootimg *b, const char *dest)
return res;
}
int libbootimg_load_config(struct bootimg *b, const char *src, int *error_line)
{
FILE *f = fopen(src, "r");
if(!f)
return -errno;
int res = 0;
int line_num = 0;
char line[1024];
while(fgets(line, sizeof(line), f))
{
if(libbootimg_load_config_line(b, line) < 0)
{
res = -1;
if(error_line)
*error_line = line_num;
goto exit;
}
++line_num;
}
exit:
fclose(f);
return res;
}
int libbootimg_load_config_line(struct bootimg *b, char *line)
{
char *s, *e;
char *name_e;
char *arg_s;
size_t n_to_cmp;
for(s = line; isspace(*s); ++s);
for(e = s+strlen(s)-1; isspace(*e); --e)
*e = 0;
if(*s == 0 || (name_e = strchr(s, '=')) == NULL)
return 0;
arg_s = name_e+1;
for(; isspace(*(name_e-1)) && name_e > s; --name_e);
for(; isspace(*arg_s); ++arg_s);
n_to_cmp = name_e-s;
if(strncmp("bootsize", s, n_to_cmp) == 0)
b->size = strtoll(arg_s, NULL, 16);
else if(strncmp("pagesize", s, n_to_cmp) == 0)
b->hdr.page_size = strtoll(arg_s, NULL, 16);
else if(strncmp("kerneladdr", s, n_to_cmp) == 0)
b->hdr.kernel_addr = strtoll(arg_s, NULL, 16);
else if(strncmp("ramdiskaddr", s, n_to_cmp) == 0)
b->hdr.ramdisk_addr = strtoll(arg_s, NULL, 16);
else if(strncmp("secondaddr", s, n_to_cmp) == 0)
b->hdr.second_addr = strtoll(arg_s, NULL, 16);
else if(strncmp("tagsaddr", s, n_to_cmp) == 0)
b->hdr.tags_addr = strtoll(arg_s, NULL, 16);
else if(strncmp("name", s, n_to_cmp) == 0)
strncpy((char*)b->hdr.name, arg_s, BOOT_NAME_SIZE);
else if(strncmp("cmdline", s, n_to_cmp) == 0)
strncpy((char*)b->hdr.cmdline, arg_s, BOOT_ARGS_SIZE);
else
return -1;
return 0;
}
int libbootimg_write_config(struct bootimg *b, const char *dst)
{
FILE *f = fopen(dst, "w");
if(!f)
return -errno;
int res = fprintf(f,
"bootsize = 0x%X\n"
"pagesize = 0x%X\n"
"kerneladdr = 0x%X\n"
"ramdiskaddr = 0x%X\n"
"secondaddr = 0x%X\n"
"tagsaddr = 0x%X\n"
"name = %s\n"
"cmdline = %s\n",
b->size, b->hdr.page_size, b->hdr.kernel_addr, b->hdr.ramdisk_addr,
b->hdr.second_addr, b->hdr.tags_addr, b->hdr.name, b->hdr.cmdline);
fclose(f);
return res;
}
uint32_t libbootimg_version(void)
{
return LIBBOOTIMG_VERSION;
@@ -504,3 +399,23 @@ const char *libbootimg_version_str(void)
{
return LIBBOOTIMG_VERSION_STR;
}
const char *libbootimg_error_str(int error)
{
switch(error)
{
case LIBBOOTIMG_SUCCESS: return "No errors";
case LIBBOOTIMG_ERROR_IO: return "Input/output error";
case LIBBOOTIMG_ERROR_ACCESS: return "Permission denied";
case LIBBOOTIMG_ERROR_NOT_FOUND: return "No such file or directory";
case LIBBOOTIMG_ERROR_INVALID_MAGIC: return "Corrupted boot image, invalid magic";
case LIBBOOTIMG_ERROR_IMG_EOF: return "Corrupted boot image, premature end of file";
case LIBBOOTIMG_ERROR_NO_BLOB_DATA: return "No data loaded into this blob";
case LIBBOOTIMG_ERROR_FILE_TOO_BIG: return "File is too big";
case LIBBOOTIMG_ERROR_MISSING_BLOB: return "Missing required blob (kernel or ramdisk)";
case LIBBOOTIMG_ERROR_INVALID_PAGESIZE: return "Invalid pagesize value";
case LIBBOOTIMG_ERROR_OTHER: return "Unhandled error";
default: return "Unknown error";
}
}