700 lines
22 KiB
C
700 lines
22 KiB
C
|
/*
|
||
|
* Copyright (C) 2008 Nokia Corporation
|
||
|
* Copyright (c) International Business Machines Corp., 2006
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Generate UBI images.
|
||
|
*
|
||
|
* Authors: Artem Bityutskiy
|
||
|
* Oliver Lohmann
|
||
|
*/
|
||
|
|
||
|
#define PROGRAM_NAME "ubinize"
|
||
|
|
||
|
#include <sys/stat.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <getopt.h>
|
||
|
#include <string.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <linux/list.h>
|
||
|
|
||
|
#include <mtd/ubi-media.h>
|
||
|
#include <libubigen.h>
|
||
|
#include <libiniparser.h>
|
||
|
#include <libubi.h>
|
||
|
#include "common.h"
|
||
|
#include "ubiutils-common.h"
|
||
|
#include "include/ubi-fastmap.h"
|
||
|
|
||
|
static const char doc[] = PROGRAM_NAME " version " VERSION
|
||
|
" - a tool to generate UBI images. An UBI image may contain one or more UBI "
|
||
|
"volumes which have to be defined in the input configuration ini-file. The "
|
||
|
"ini file defines all the UBI volumes - their characteristics and the "
|
||
|
"contents, but it does not define the characteristics of the flash the UBI "
|
||
|
"image is generated for. Instead, the flash characteristics are defined via "
|
||
|
"the command-line options. Note, if not sure about some of the command-line "
|
||
|
"parameters, do not specify them and let the utility use default values.";
|
||
|
|
||
|
static const char optionsstr[] =
|
||
|
"-o, --output=<file name> output file name\n"
|
||
|
"-p, --peb-size=<bytes> size of the physical eraseblock of the flash\n"
|
||
|
" this UBI image is created for in bytes,\n"
|
||
|
" kilobytes (KiB), or megabytes (MiB)\n"
|
||
|
" (mandatory parameter)\n"
|
||
|
"-m, --min-io-size=<bytes> minimum input/output unit size of the flash\n"
|
||
|
" in bytes\n"
|
||
|
"-s, --sub-page-size=<bytes> minimum input/output unit used for UBI\n"
|
||
|
" headers, e.g. sub-page size in case of NAND\n"
|
||
|
" flash (equivalent to the minimum input/output\n"
|
||
|
" unit size by default)\n"
|
||
|
"-O, --vid-hdr-offset=<num> offset if the VID header from start of the\n"
|
||
|
" physical eraseblock (default is the next\n"
|
||
|
" minimum I/O unit or sub-page after the EC\n"
|
||
|
" header)\n"
|
||
|
"-e, --erase-counter=<num> the erase counter value to put to EC headers\n"
|
||
|
" (default is 0)\n"
|
||
|
"-c, --max-leb-cnt=<num> maximum logical erase block count\n"
|
||
|
"-x, --ubi-ver=<num> UBI version number to put to EC headers\n"
|
||
|
" (default is 1)\n"
|
||
|
"-Q, --image-seq=<num> 32-bit UBI image sequence number to use\n"
|
||
|
" (by default a random number is picked)\n"
|
||
|
"-v, --verbose be verbose\n"
|
||
|
"-h, --help print help message\n"
|
||
|
"-V, --version print program version";
|
||
|
|
||
|
static const char usage[] =
|
||
|
"Usage: " PROGRAM_NAME " [-o filename] [-p <bytes>] [-m <bytes>] [-s <bytes>] [-O <num>] [-e <num>]\n"
|
||
|
"\t\t[-x <num>] [-Q <num>] [-v] [-h] [-V] [--output=<filename>] [--peb-size=<bytes>]\n"
|
||
|
"\t\t[--min-io-size=<bytes>] [--sub-page-size=<bytes>] [--vid-hdr-offset=<num>]\n"
|
||
|
"\t\t[--erase-counter=<num>] [--max-leb-cnt=<num>] [--ubi-ver=<num>] [--image-seq=<num>]\n"
|
||
|
"\t\t[--verbose] [--help] [--version] ini-file\n"
|
||
|
"Example: " PROGRAM_NAME " -o ubi.img -p 16KiB -m 512 -s 256 cfg.ini - create UBI image\n"
|
||
|
" 'ubi.img' as described by configuration file 'cfg.ini'";
|
||
|
|
||
|
static const char ini_doc[] = "INI-file format.\n"
|
||
|
"The input configuration ini-file describes all the volumes which have to\n"
|
||
|
"be included to the output UBI image. Each volume is described in its own\n"
|
||
|
"section which may be named arbitrarily. The section consists on\n"
|
||
|
"\"key=value\" pairs, for example:\n\n"
|
||
|
"[jffs2-volume]\n"
|
||
|
"mode=ubi\n"
|
||
|
"image=../jffs2.img\n"
|
||
|
"vol_id=1\n"
|
||
|
"vol_size=30MiB\n"
|
||
|
"vol_type=dynamic\n"
|
||
|
"vol_name=jffs2_volume\n"
|
||
|
"vol_flags=autoresize\n"
|
||
|
"vol_alignment=1\n\n"
|
||
|
"This example configuration file tells the utility to create an UBI image\n"
|
||
|
"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n"
|
||
|
"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n"
|
||
|
"\"image=../jffs2.img\" line tells the utility to take the contents of the\n"
|
||
|
"volume from the \"../jffs2.img\" file. The size of the image file has to be\n"
|
||
|
"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n"
|
||
|
"mandatory and just tells that the section describes an UBI volume - other\n"
|
||
|
"section modes may be added in the future.\n"
|
||
|
"Notes:\n"
|
||
|
" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n"
|
||
|
" gigabytes (GiB) or bytes (no modifier);\n"
|
||
|
" * if \"vol_size\" key is absent, the volume size is assumed to be\n"
|
||
|
" equivalent to the size of the image file (defined by \"image\" key);\n"
|
||
|
" * if the \"image\" is absent, the volume is assumed to be empty;\n"
|
||
|
" * volume alignment must not be greater than the logical eraseblock size;\n"
|
||
|
" * one ini file may contain arbitrary number of sections, the utility will\n"
|
||
|
" put all the volumes which are described by these section to the output\n"
|
||
|
" UBI image file.";
|
||
|
|
||
|
static const struct option long_options[] = {
|
||
|
{ .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
|
||
|
{ .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' },
|
||
|
{ .name = "max-leb-cnt", .has_arg = 1, .flag = NULL, .val = 'c' },
|
||
|
{ .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' },
|
||
|
{ .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' },
|
||
|
{ .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' },
|
||
|
{ .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' },
|
||
|
{ .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' },
|
||
|
{ .name = "image-seq", .has_arg = 1, .flag = NULL, .val = 'Q' },
|
||
|
{ .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
|
||
|
{ .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
|
||
|
{ .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
|
||
|
{ NULL, 0, NULL, 0}
|
||
|
};
|
||
|
|
||
|
struct args {
|
||
|
const char *f_in;
|
||
|
const char *f_out;
|
||
|
int out_fd;
|
||
|
int peb_size;
|
||
|
int max_leb_count;
|
||
|
int min_io_size;
|
||
|
int subpage_size;
|
||
|
int vid_hdr_offs;
|
||
|
int ec;
|
||
|
int ubi_ver;
|
||
|
uint32_t image_seq;
|
||
|
int verbose;
|
||
|
dictionary *dict;
|
||
|
};
|
||
|
|
||
|
static struct args args = {
|
||
|
.peb_size = -1,
|
||
|
.max_leb_count = -1,
|
||
|
.min_io_size = -1,
|
||
|
.subpage_size = -1,
|
||
|
.ubi_ver = 1,
|
||
|
};
|
||
|
|
||
|
static int parse_opt(int argc, char * const argv[])
|
||
|
{
|
||
|
ubiutils_srand();
|
||
|
args.image_seq = rand();
|
||
|
|
||
|
while (1) {
|
||
|
int key, error = 0;
|
||
|
unsigned long int image_seq;
|
||
|
|
||
|
key = getopt_long(argc, argv, "o:p:c:m:s:O:e:x:Q:vhV", long_options, NULL);
|
||
|
if (key == -1)
|
||
|
break;
|
||
|
|
||
|
switch (key) {
|
||
|
case 'o':
|
||
|
args.out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY,
|
||
|
S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH);
|
||
|
if (args.out_fd == -1)
|
||
|
return sys_errmsg("cannot open file \"%s\"", optarg);
|
||
|
args.f_out = optarg;
|
||
|
break;
|
||
|
|
||
|
case 'p':
|
||
|
args.peb_size = ubiutils_get_bytes(optarg);
|
||
|
if (args.peb_size <= 0)
|
||
|
return errmsg("bad physical eraseblock size: \"%s\"", optarg);
|
||
|
break;
|
||
|
case 'c':
|
||
|
args.max_leb_count = ubiutils_get_bytes(optarg);
|
||
|
if (args.max_leb_count <= 0)
|
||
|
return errmsg("bad maximum LEB count: \"%s\"", optarg);
|
||
|
break;
|
||
|
case 'm':
|
||
|
args.min_io_size = ubiutils_get_bytes(optarg);
|
||
|
if (args.min_io_size <= 0)
|
||
|
return errmsg("bad min. I/O unit size: \"%s\"", optarg);
|
||
|
if (!is_power_of_2(args.min_io_size))
|
||
|
return errmsg("min. I/O unit size should be power of 2");
|
||
|
break;
|
||
|
|
||
|
case 's':
|
||
|
args.subpage_size = ubiutils_get_bytes(optarg);
|
||
|
if (args.subpage_size <= 0)
|
||
|
return errmsg("bad sub-page size: \"%s\"", optarg);
|
||
|
if (!is_power_of_2(args.subpage_size))
|
||
|
return errmsg("sub-page size should be power of 2");
|
||
|
break;
|
||
|
|
||
|
case 'O':
|
||
|
args.vid_hdr_offs = simple_strtoul(optarg, &error);
|
||
|
if (error || args.vid_hdr_offs < 0)
|
||
|
return errmsg("bad VID header offset: \"%s\"", optarg);
|
||
|
break;
|
||
|
|
||
|
case 'e':
|
||
|
args.ec = simple_strtoul(optarg, &error);
|
||
|
if (error || args.ec < 0)
|
||
|
return errmsg("bad erase counter value: \"%s\"", optarg);
|
||
|
break;
|
||
|
|
||
|
case 'x':
|
||
|
args.ubi_ver = simple_strtoul(optarg, &error);
|
||
|
if (error || args.ubi_ver < 0)
|
||
|
return errmsg("bad UBI version: \"%s\"", optarg);
|
||
|
break;
|
||
|
|
||
|
case 'Q':
|
||
|
image_seq = simple_strtoul(optarg, &error);
|
||
|
if (error || image_seq > 0xFFFFFFFF)
|
||
|
return errmsg("bad UBI image sequence number: \"%s\"", optarg);
|
||
|
args.image_seq = image_seq;
|
||
|
break;
|
||
|
|
||
|
case 'v':
|
||
|
args.verbose = 1;
|
||
|
break;
|
||
|
|
||
|
case 'h':
|
||
|
ubiutils_print_text(stdout, doc, 80);
|
||
|
printf("\n%s\n\n", ini_doc);
|
||
|
printf("%s\n\n", usage);
|
||
|
printf("%s\n", optionsstr);
|
||
|
exit(EXIT_SUCCESS);
|
||
|
|
||
|
case 'V':
|
||
|
common_print_version();
|
||
|
exit(EXIT_SUCCESS);
|
||
|
|
||
|
default:
|
||
|
fprintf(stderr, "Use -h for help\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (optind == argc)
|
||
|
return errmsg("input configuration file was not specified (use -h for help)");
|
||
|
|
||
|
if (optind != argc - 1)
|
||
|
return errmsg("more then one configuration file was specified (use -h for help)");
|
||
|
|
||
|
args.f_in = argv[optind];
|
||
|
|
||
|
if (args.peb_size < 0)
|
||
|
return errmsg("physical eraseblock size was not specified (use -h for help)");
|
||
|
|
||
|
if (args.peb_size > UBI_MAX_PEB_SZ)
|
||
|
return errmsg("too high physical eraseblock size %d", args.peb_size);
|
||
|
|
||
|
if (args.min_io_size < 0)
|
||
|
return errmsg("min. I/O unit size was not specified (use -h for help)");
|
||
|
|
||
|
if (args.subpage_size < 0)
|
||
|
args.subpage_size = args.min_io_size;
|
||
|
|
||
|
if (args.subpage_size > args.min_io_size)
|
||
|
return errmsg("sub-page cannot be larger then min. I/O unit");
|
||
|
|
||
|
if (args.peb_size % args.min_io_size)
|
||
|
return errmsg("physical eraseblock should be multiple of min. I/O units");
|
||
|
|
||
|
if (args.min_io_size % args.subpage_size)
|
||
|
return errmsg("min. I/O unit size should be multiple of sub-page size");
|
||
|
|
||
|
if (!args.f_out)
|
||
|
return errmsg("output file was not specified (use -h for help)");
|
||
|
|
||
|
if (args.vid_hdr_offs) {
|
||
|
if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE >= args.peb_size)
|
||
|
return errmsg("bad VID header position");
|
||
|
if (args.vid_hdr_offs % 8)
|
||
|
return errmsg("VID header offset has to be multiple of min. I/O unit size");
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int read_section(const struct ubigen_info *ui, const char *sname,
|
||
|
struct ubigen_vol_info *vi, const char **img,
|
||
|
struct stat *st)
|
||
|
{
|
||
|
char buf[256];
|
||
|
const char *p;
|
||
|
|
||
|
*img = NULL;
|
||
|
|
||
|
if (strlen(sname) > 128)
|
||
|
return errmsg("too long section name \"%s\"", sname);
|
||
|
|
||
|
/* Make sure mode is UBI, otherwise ignore this section */
|
||
|
sprintf(buf, "%s:mode", sname);
|
||
|
p = iniparser_getstring(args.dict, buf, NULL);
|
||
|
if (!p) {
|
||
|
errmsg("\"mode\" key not found in section \"%s\"", sname);
|
||
|
errmsg("the \"mode\" key is mandatory and has to be "
|
||
|
"\"mode=ubi\" if the section describes an UBI volume");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* If mode is not UBI, skip this section */
|
||
|
if (strcmp(p, "ubi")) {
|
||
|
verbose(args.verbose, "skip non-ubi section \"%s\"", sname);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "mode=ubi, keep parsing");
|
||
|
|
||
|
/* Fetch volume type */
|
||
|
sprintf(buf, "%s:vol_type", sname);
|
||
|
p = iniparser_getstring(args.dict, buf, NULL);
|
||
|
if (!p) {
|
||
|
normsg("volume type was not specified in "
|
||
|
"section \"%s\", assume \"dynamic\"\n", sname);
|
||
|
vi->type = UBI_VID_DYNAMIC;
|
||
|
} else {
|
||
|
if (!strcmp(p, "static"))
|
||
|
vi->type = UBI_VID_STATIC;
|
||
|
else if (!strcmp(p, "dynamic"))
|
||
|
vi->type = UBI_VID_DYNAMIC;
|
||
|
else
|
||
|
return errmsg("invalid volume type \"%s\" in section \"%s\"",
|
||
|
p, sname);
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "volume type: %s",
|
||
|
vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static");
|
||
|
|
||
|
/* Fetch the name of the volume image file */
|
||
|
sprintf(buf, "%s:image", sname);
|
||
|
p = iniparser_getstring(args.dict, buf, NULL);
|
||
|
if (p) {
|
||
|
*img = p;
|
||
|
if (stat(p, st))
|
||
|
return sys_errmsg("cannot stat \"%s\" referred from section \"%s\"",
|
||
|
p, sname);
|
||
|
if (st->st_size == 0)
|
||
|
return errmsg("empty file \"%s\" referred from section \"%s\"",
|
||
|
p, sname);
|
||
|
} else if (vi->type == UBI_VID_STATIC)
|
||
|
return errmsg("image is not specified for static volume in section \"%s\"",
|
||
|
sname);
|
||
|
|
||
|
/* Fetch volume id */
|
||
|
sprintf(buf, "%s:vol_id", sname);
|
||
|
vi->id = iniparser_getint(args.dict, buf, -1);
|
||
|
if (vi->id == -1)
|
||
|
return errmsg("\"vol_id\" key not found in section \"%s\"", sname);
|
||
|
if (vi->id < 0)
|
||
|
return errmsg("negative volume ID %d in section \"%s\"",
|
||
|
vi->id, sname);
|
||
|
if (vi->id >= ui->max_volumes)
|
||
|
return errmsg("too high volume ID %d in section \"%s\", max. is %d",
|
||
|
vi->id, sname, ui->max_volumes);
|
||
|
|
||
|
verbose(args.verbose, "volume ID: %d", vi->id);
|
||
|
|
||
|
/* Fetch volume size */
|
||
|
sprintf(buf, "%s:vol_size", sname);
|
||
|
p = iniparser_getstring(args.dict, buf, NULL);
|
||
|
if (p) {
|
||
|
vi->bytes = ubiutils_get_bytes(p);
|
||
|
if (vi->bytes <= 0)
|
||
|
return errmsg("bad \"vol_size\" key value \"%s\" (section \"%s\")",
|
||
|
p, sname);
|
||
|
|
||
|
/* Make sure the image size is not larger than volume size */
|
||
|
if (*img && st->st_size > vi->bytes)
|
||
|
return errmsg("error in section \"%s\": size of the image file "
|
||
|
"\"%s\" is %lld, which is larger than volume size %lld",
|
||
|
sname, *img, (long long)st->st_size, vi->bytes);
|
||
|
verbose(args.verbose, "volume size: %lld bytes", vi->bytes);
|
||
|
} else {
|
||
|
struct stat st;
|
||
|
|
||
|
if (!*img)
|
||
|
return errmsg("neither image file (\"image=\") nor volume size "
|
||
|
"(\"vol_size=\") specified in section \"%s\"", sname);
|
||
|
|
||
|
if (stat(*img, &st))
|
||
|
return sys_errmsg("cannot stat \"%s\"", *img);
|
||
|
|
||
|
vi->bytes = st.st_size;
|
||
|
|
||
|
if (vi->bytes == 0)
|
||
|
return errmsg("file \"%s\" referred from section \"%s\" is empty",
|
||
|
*img, sname);
|
||
|
|
||
|
normsg_cont("volume size was not specified in section \"%s\", assume"
|
||
|
" minimum to fit image \"%s\"", sname, *img);
|
||
|
ubiutils_print_bytes(vi->bytes, 1);
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
/* Fetch volume name */
|
||
|
sprintf(buf, "%s:vol_name", sname);
|
||
|
p = iniparser_getstring(args.dict, buf, NULL);
|
||
|
if (!p)
|
||
|
return errmsg("\"vol_name\" key not found in section \"%s\"", sname);
|
||
|
|
||
|
vi->name = p;
|
||
|
vi->name_len = strlen(p);
|
||
|
if (vi->name_len > UBI_VOL_NAME_MAX)
|
||
|
return errmsg("too long volume name in section \"%s\", max. is %d characters",
|
||
|
vi->name, UBI_VOL_NAME_MAX);
|
||
|
|
||
|
verbose(args.verbose, "volume name: %s", p);
|
||
|
|
||
|
/* Fetch volume alignment */
|
||
|
sprintf(buf, "%s:vol_alignment", sname);
|
||
|
vi->alignment = iniparser_getint(args.dict, buf, -1);
|
||
|
if (vi->alignment == -1)
|
||
|
vi->alignment = 1;
|
||
|
else if (vi->id < 0)
|
||
|
return errmsg("negative volume alignement %d in section \"%s\"",
|
||
|
vi->alignment, sname);
|
||
|
|
||
|
verbose(args.verbose, "volume alignment: %d", vi->alignment);
|
||
|
|
||
|
/* Fetch volume flags */
|
||
|
sprintf(buf, "%s:vol_flags", sname);
|
||
|
p = iniparser_getstring(args.dict, buf, NULL);
|
||
|
if (p) {
|
||
|
if (!strcmp(p, "autoresize")) {
|
||
|
verbose(args.verbose, "autoresize flags found");
|
||
|
vi->flags |= UBI_VTBL_AUTORESIZE_FLG;
|
||
|
} else {
|
||
|
return errmsg("unknown flags \"%s\" in section \"%s\"",
|
||
|
p, sname);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Initialize the rest of the volume information */
|
||
|
vi->data_pad = ui->leb_size % vi->alignment;
|
||
|
vi->usable_leb_size = ui->leb_size - vi->data_pad;
|
||
|
if (vi->type == UBI_VID_DYNAMIC)
|
||
|
vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size;
|
||
|
else
|
||
|
vi->used_ebs = (st->st_size + vi->usable_leb_size - 1) / vi->usable_leb_size;
|
||
|
vi->compat = 0;
|
||
|
if (ui->max_leb_count > 0) {
|
||
|
vi->pebs = calloc(sizeof(int), ui->max_leb_count);
|
||
|
if (!vi->pebs) {
|
||
|
sys_errmsg("cannot allocate memory for vi->peb\n");
|
||
|
return -1;
|
||
|
}
|
||
|
memset(vi->pebs, -1, sizeof(int)*ui->max_leb_count);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char * const argv[])
|
||
|
{
|
||
|
int err = -1, sects, i, autoresize_was_already = 0;
|
||
|
struct ubigen_info ui;
|
||
|
struct ubi_vtbl_record *vtbl;
|
||
|
struct ubigen_vol_info *vi;
|
||
|
int used_cnt = 0;
|
||
|
struct list_head used;
|
||
|
struct ubi_wl_peb *new_peb;
|
||
|
|
||
|
off_t seek;
|
||
|
|
||
|
err = parse_opt(argc, argv);
|
||
|
if (err)
|
||
|
return -1;
|
||
|
|
||
|
ubigen_info_init(&ui, args.peb_size, args.min_io_size,
|
||
|
args.subpage_size, args.vid_hdr_offs,
|
||
|
args.ubi_ver, args.image_seq, args.max_leb_count);
|
||
|
|
||
|
verbose(args.verbose, "LEB size: %d", ui.leb_size);
|
||
|
verbose(args.verbose, "PEB size: %d", ui.peb_size);
|
||
|
verbose(args.verbose, "min. I/O size: %d", ui.min_io_size);
|
||
|
verbose(args.verbose, "sub-page size: %d", args.subpage_size);
|
||
|
verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs);
|
||
|
verbose(args.verbose, "data offset: %d", ui.data_offs);
|
||
|
verbose(args.verbose, "UBI image sequence number: %u", ui.image_seq);
|
||
|
if (args.max_leb_count > 0)
|
||
|
verbose(args.verbose, "maximum logical erase block count: %d", args.max_leb_count);
|
||
|
|
||
|
vtbl = ubigen_create_empty_vtbl(&ui);
|
||
|
if (!vtbl)
|
||
|
goto out;
|
||
|
|
||
|
args.dict = iniparser_load(args.f_in);
|
||
|
if (!args.dict) {
|
||
|
errmsg("cannot load the input ini file \"%s\"", args.f_in);
|
||
|
goto out_vtbl;
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in);
|
||
|
|
||
|
/* Each section describes one volume */
|
||
|
sects = iniparser_getnsec(args.dict);
|
||
|
if (sects == -1) {
|
||
|
errmsg("ini-file parsing error (iniparser_getnsec)");
|
||
|
goto out_dict;
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "count of sections: %d", sects);
|
||
|
if (sects == 0) {
|
||
|
errmsg("no sections found the ini-file \"%s\"", args.f_in);
|
||
|
goto out_dict;
|
||
|
}
|
||
|
|
||
|
if (sects > ui.max_volumes) {
|
||
|
errmsg("too many sections (%d) in the ini-file \"%s\"",
|
||
|
sects, args.f_in);
|
||
|
normsg("each section corresponds to an UBI volume, maximum "
|
||
|
"count of volumes is %d", ui.max_volumes);
|
||
|
goto out_dict;
|
||
|
}
|
||
|
|
||
|
/* Save vi[0] for layout volume */
|
||
|
vi = calloc(sizeof(struct ubigen_vol_info), sects + 1);
|
||
|
if (!vi) {
|
||
|
errmsg("cannot allocate memory");
|
||
|
goto out_dict;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Skip 2 PEBs at the beginning of the file for the volume table which
|
||
|
* will be written later.
|
||
|
*/
|
||
|
seek = ui.peb_size * 2;
|
||
|
used_cnt += 2;
|
||
|
if (lseek(args.out_fd, seek, SEEK_SET) != seek) {
|
||
|
sys_errmsg("cannot seek file \"%s\"", args.f_out);
|
||
|
goto out_free;
|
||
|
}
|
||
|
|
||
|
/* If max_leb_count was provided, leave one PEB for FM superblock */
|
||
|
if (ui.max_leb_count > 0) {
|
||
|
seek = ui.peb_size * 3;
|
||
|
used_cnt++;
|
||
|
if (lseek(args.out_fd, seek, SEEK_SET) != seek) {
|
||
|
sys_errmsg("cannot seek file \"%s\"", args.f_out);
|
||
|
goto out_free;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
INIT_LIST_HEAD(&used);
|
||
|
for (i = 0; i < sects; i++) {
|
||
|
const char *sname = iniparser_getsecname(args.dict, i);
|
||
|
const char *img = NULL;
|
||
|
struct stat st;
|
||
|
int fd, j;
|
||
|
|
||
|
if (!sname) {
|
||
|
errmsg("ini-file parsing error (iniparser_getsecname)");
|
||
|
goto out_free;
|
||
|
}
|
||
|
|
||
|
if (args.verbose)
|
||
|
printf("\n");
|
||
|
verbose(args.verbose, "parsing section \"%s\"", sname);
|
||
|
|
||
|
err = read_section(&ui, sname, &vi[i], &img, &st);
|
||
|
if (err == -1)
|
||
|
goto out_free;
|
||
|
|
||
|
verbose(args.verbose, "adding volume %d", vi[i].id);
|
||
|
|
||
|
/*
|
||
|
* Make sure that volume ID and name is unique and that only
|
||
|
* one volume has auto-resize flag
|
||
|
*/
|
||
|
for (j = 0; j < i; j++) {
|
||
|
if (vi[i].id == vi[j].id) {
|
||
|
errmsg("volume IDs must be unique, but ID %d "
|
||
|
"in section \"%s\" is not",
|
||
|
vi[i].id, sname);
|
||
|
goto out_free;
|
||
|
}
|
||
|
|
||
|
if (!strcmp(vi[i].name, vi[j].name)) {
|
||
|
errmsg("volume name must be unique, but name "
|
||
|
"\"%s\" in section \"%s\" is not",
|
||
|
vi[i].name, sname);
|
||
|
goto out_free;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (vi[i].flags & UBI_VTBL_AUTORESIZE_FLG) {
|
||
|
if (autoresize_was_already)
|
||
|
return errmsg("only one volume is allowed "
|
||
|
"to have auto-resize flag");
|
||
|
autoresize_was_already = 1;
|
||
|
}
|
||
|
|
||
|
err = ubigen_add_volume(&ui, &vi[i], vtbl);
|
||
|
if (err) {
|
||
|
errmsg("cannot add volume for section \"%s\"", sname);
|
||
|
goto out_free;
|
||
|
}
|
||
|
|
||
|
if (img) {
|
||
|
fd = open(img, O_RDONLY);
|
||
|
if (fd == -1) {
|
||
|
sys_errmsg("cannot open \"%s\"", img);
|
||
|
goto out_free;
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "writing volume %d", vi[i].id);
|
||
|
verbose(args.verbose, "image file: %s", img);
|
||
|
|
||
|
err = ubigen_write_volume(&ui, &vi[i], args.ec,
|
||
|
st.st_size, fd, args.out_fd,
|
||
|
&used, &used_cnt);
|
||
|
close(fd);
|
||
|
if (err) {
|
||
|
errmsg("cannot write volume for section \"%s\"", sname);
|
||
|
goto out_free;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (args.verbose)
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "writing layout volume");
|
||
|
|
||
|
err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl,
|
||
|
args.out_fd, &vi[sects]);
|
||
|
if (err) {
|
||
|
errmsg("cannot write layout volume");
|
||
|
goto out_free;
|
||
|
} else
|
||
|
verbose(args.verbose, "writing layout volume - done");
|
||
|
|
||
|
if (ui.max_leb_count > 0) {
|
||
|
vi[sects].pebs = calloc(sizeof(int), ui.max_leb_count);
|
||
|
if (!vi[sects].pebs) {
|
||
|
sys_errmsg("cannot allocate memory for vi->peb\n");
|
||
|
goto out_free;
|
||
|
}
|
||
|
memset(vi[sects].pebs, -1, sizeof(int)*ui.max_leb_count);
|
||
|
/* Add layout volume PEBs to used list */
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
new_peb = malloc(sizeof(*new_peb));
|
||
|
if (!new_peb) {
|
||
|
sys_errmsg("mem allocation failed");
|
||
|
goto out_free;
|
||
|
}
|
||
|
new_peb->pnum = i;
|
||
|
new_peb->ec = args.ec;
|
||
|
vi[sects].pebs[i] = i;
|
||
|
}
|
||
|
vi[sects].reserved_pebs = 2;
|
||
|
add_fastmap_data(&ui, 2, used_cnt-1, args.ec, &used,
|
||
|
vi, sects+1, args.out_fd);
|
||
|
list_for_each_entry(new_peb, &used, list)
|
||
|
free(new_peb);
|
||
|
for (i = 0; i < sects; i++)
|
||
|
free(vi[i].pebs);
|
||
|
}
|
||
|
|
||
|
verbose(args.verbose, "done");
|
||
|
free(vi);
|
||
|
iniparser_freedict(args.dict);
|
||
|
free(vtbl);
|
||
|
close(args.out_fd);
|
||
|
return 0;
|
||
|
|
||
|
out_free:
|
||
|
list_for_each_entry(new_peb, &used, list)
|
||
|
free(new_peb);
|
||
|
for (i = 0; i < sects; i++)
|
||
|
free(vi[i].pebs);
|
||
|
free(vi);
|
||
|
out_dict:
|
||
|
iniparser_freedict(args.dict);
|
||
|
out_vtbl:
|
||
|
free(vtbl);
|
||
|
out:
|
||
|
close(args.out_fd);
|
||
|
remove(args.f_out);
|
||
|
return err;
|
||
|
}
|