blob: 5c1cd3221c3a89c8ab2bbda1240544a4cfdddc25 [file] [log] [blame]
/*
* 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.
*/
/*
* An utility to update UBI volumes.
*
* Authors: Frank Haverkamp
* Joshua W. Boyer
* Artem Bityutskiy
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <libubi.h>
#include "common.h"
#define PROGRAM_VERSION "1.2"
#define PROGRAM_NAME "ubiupdatevol"
struct args {
int truncate;
const char *node;
const char *img;
/* For deprecated -d and -B options handling */
char dev_name[256];
int size;
int use_stdin;
};
static struct args args;
static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
" - a tool to write data to UBI volumes.";
static const char *optionsstr =
"-t, --truncate truncate volume (wipe it out)\n"
"-s, --size=<bytes> bytes in input, if not reading from file\n"
"-h, --help print help message\n"
"-V, --version print program version";
static const char *usage =
"Usage: " PROGRAM_NAME " <UBI volume node file name> [-t] [-s <size>] [-h] [-V] [--truncate]\n"
"\t\t\t[--size=<size>] [--help] [--version] <image file>\n\n"
"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n"
"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1";
struct option long_options[] = {
{ .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' },
{ .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
{ .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
{ .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
{ NULL, 0, NULL, 0}
};
static int parse_opt(int argc, char * const argv[])
{
while (1) {
int key;
char *endp;
key = getopt_long(argc, argv, "ts:h?V", long_options, NULL);
if (key == -1)
break;
switch (key) {
case 't':
args.truncate = 1;
break;
case 's':
args.size = strtoul(optarg, &endp, 0);
if (*endp != '\0' || endp == optarg || args.size < 0)
return errmsg("bad size: " "\"%s\"", optarg);
break;
case 'h':
case '?':
fprintf(stderr, "%s\n\n", doc);
fprintf(stderr, "%s\n\n", usage);
fprintf(stderr, "%s\n", optionsstr);
exit(EXIT_SUCCESS);
case 'V':
fprintf(stderr, "%s\n", PROGRAM_VERSION);
exit(EXIT_SUCCESS);
case ':':
return errmsg("parameter is missing");
default:
fprintf(stderr, "Use -h for help\n");
return -1;
}
}
if (optind == argc)
return errmsg("UBI device name was not specified (use -h for help)");
else if (optind != argc - 2 && !args.truncate)
return errmsg("specify UBI device name and image file name as first 2 "
"parameters (use -h for help)");
args.node = argv[optind];
args.img = argv[optind + 1];
if (args.img && args.truncate)
return errmsg("You can't truncate and specify an image (use -h for help)");
if (args.img && !args.truncate) {
if (strcmp(args.img, "-") == 0)
args.use_stdin = 1;
if (args.use_stdin && !args.size)
return errmsg("file size must be specified if input is stdin");
}
return 0;
}
static int truncate_volume(libubi_t libubi)
{
int err, fd;
fd = open(args.node, O_RDWR);
if (fd == -1)
return sys_errmsg("cannot open \"%s\"", args.node);
err = ubi_update_start(libubi, fd, 0);
if (err) {
sys_errmsg("cannot truncate volume \"%s\"", args.node);
close(fd);
return -1;
}
close(fd);
return 0;
}
static int ubi_write(int fd, const void *buf, int len)
{
int ret;
while (len) {
ret = write(fd, buf, len);
if (ret < 0) {
if (errno == EINTR) {
warnmsg("do not interrupt me!");
continue;
}
return sys_errmsg("cannot write %d bytes to volume \"%s\"",
len, args.node);
}
if (ret == 0)
return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node);
len -= ret;
buf += ret;
}
return 0;
}
static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info)
{
int err, fd, ifd;
long long bytes;
char *buf;
buf = malloc(vol_info->leb_size);
if (!buf)
return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size);
if (!args.size) {
struct stat st;
err = stat(args.img, &st);
if (err < 0) {
errmsg("stat failed on \"%s\"", args.img);
goto out_free;
}
bytes = st.st_size;
} else
bytes = args.size;
if (bytes > vol_info->rsvd_bytes) {
errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)",
args.img, bytes, args.node, vol_info->rsvd_bytes);
goto out_free;
}
fd = open(args.node, O_RDWR);
if (fd == -1) {
sys_errmsg("cannot open UBI volume \"%s\"", args.node);
goto out_free;
}
if (args.use_stdin)
ifd = STDIN_FILENO;
else {
ifd = open(args.img, O_RDONLY);
if (ifd == -1) {
sys_errmsg("cannot open \"%s\"", args.img);
goto out_close1;
}
}
err = ubi_update_start(libubi, fd, bytes);
if (err) {
sys_errmsg("cannot start volume \"%s\" update", args.node);
goto out_close;
}
while (bytes) {
int ret, to_copy = vol_info->leb_size;
if (to_copy > bytes)
to_copy = bytes;
ret = read(ifd, buf, to_copy);
if (ret <= 0) {
if (errno == EINTR) {
warnmsg("do not interrupt me!");
continue;
} else {
sys_errmsg("cannot read %d bytes from \"%s\"",
to_copy, args.img);
goto out_close;
}
}
err = ubi_write(fd, buf, ret);
if (err)
goto out_close;
bytes -= ret;
}
close(ifd);
close(fd);
free(buf);
return 0;
out_close:
close(ifd);
out_close1:
close(fd);
out_free:
free(buf);
return -1;
}
int main(int argc, char * const argv[])
{
int err;
libubi_t libubi;
struct ubi_vol_info vol_info;
err = parse_opt(argc, argv);
if (err)
return -1;
libubi = libubi_open();
if (!libubi) {
if (errno == 0)
return errmsg("UBI is not present in the system");
else
return errmsg("cannot open libubi");
}
err = ubi_probe_node(libubi, args.node);
if (err == 1) {
errmsg("\"%s\" is an UBI device node, not an UBI volume node",
args.node);
goto out_libubi;
} else if (err < 0) {
if (errno == ENODEV)
errmsg("\"%s\" is not an UBI volume node", args.node);
else
sys_errmsg("error while probing \"%s\"", args.node);
goto out_libubi;
}
err = ubi_get_vol_info(libubi, args.node, &vol_info);
if (err) {
sys_errmsg("cannot get information about UBI volume \"%s\"",
args.node);
goto out_libubi;
}
if (args.truncate)
err = truncate_volume(libubi);
else
err = update_volume(libubi, &vol_info);
if (err)
goto out_libubi;
libubi_close(libubi);
return 0;
out_libubi:
libubi_close(libubi);
return -1;
}