blob: 50955a41c874999d0005a2a38f36e46e1144bd98 [file] [log] [blame]
/*
* Based originally on the ubiupdatevol utility
* Copyright (c) International Business Machines Corp., 2006
* Copyright (c) Google, Inc, 2014
*
* 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.
*/
/*
* A utility to replicate/copy UBI volumes.
*
* Authors: Frank Haverkamp
* Joshua W. Boyer
* Artem Bityutskiy
* Peter van Vugt
*/
#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 <libubi.h>
#include "common.h"
#define PROGRAM_VERSION "1.0"
#define PROGRAM_NAME "ubicpvol"
struct args {
const char *destNode;
const char *srcNode;
};
static struct args args;
static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
" - a tool to replicate/copy UBI volumes.\n";
static const char *optionsstr =
"Options:\n"
"-h, --help print help message\n"
"-V, --version print program version\n";
static const char *usage =
"Usage: " PROGRAM_NAME " [OPTIONS] <UBI source volume node> <UBI destination volume node>\n\n"
"Example: " PROGRAM_NAME " /dev/ubi0_1 /dev/ubi0_2 - copy UBI volume \"/dev/ubi0_1\" to \"/dev/ubi0_2\"\n";
static const char *notes =
"Note that both volumes must have the same LEB size and the data in the source\n"
"volume must fit into the destination volume.\n";
struct option long_options[] = {
{ .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
{ .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
{ NULL, 0, NULL, 0}
};
static int parse_opt(int argc, char * const argv[])
{
while (1) {
int key;
key = getopt_long(argc, argv, "h?V", long_options, NULL);
if (key == -1)
break;
switch (key) {
case 'h':
case '?':
fprintf(stderr, "%s\n", doc);
fprintf(stderr, "%s\n", usage);
fprintf(stderr, "%s\n", optionsstr);
fprintf(stderr, "%s\n", notes);
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 - 2)
return errmsg("specify source and destination UBI device names as first 2 "
"parameters (use -h for help)");
args.srcNode = argv[optind];
args.destNode = argv[optind + 1];
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 errmsg("error %d: cannot write %d bytes to volume \"%s\"",
errno, len, args.destNode);
}
len -= ret;
buf += ret;
}
return 0;
}
static int copy_volume(libubi_t libubi, struct ubi_vol_info *src_vol_info, struct ubi_vol_info *dest_vol_info)
{
int err, dest_fd, src_fd;
long long bytes;
char *buf;
int ret = -1;
if (src_vol_info->leb_size != dest_vol_info->leb_size) {
return errmsg("source and destination volumes have different LEB sizes");
} else if (src_vol_info->data_bytes > dest_vol_info->rsvd_bytes) {
return errmsg("source volume \"%s\" too large for destination volume \"%s\" (%lld > %lld)", args.srcNode, args.destNode, src_vol_info->data_bytes, dest_vol_info->rsvd_bytes);
}
buf = malloc(dest_vol_info->leb_size);
if (!buf)
return errmsg("cannot allocate %d bytes of memory", dest_vol_info->leb_size);
bytes = src_vol_info->data_bytes;
src_fd = open(args.srcNode, O_RDONLY);
if (src_fd == -1) {
errmsg("cannot open source UBI volume \"%s\"", args.srcNode);
goto out_free;
}
dest_fd = open(args.destNode, O_RDWR);
if (dest_fd == -1) {
errmsg("cannot open destination UBI volume \"%s\"", args.destNode);
goto out_close1;
}
err = ubi_update_start(libubi, dest_fd, bytes);
if (err) {
errmsg("cannot start volume \"%s\" update", args.destNode);
goto out_close;
}
while (bytes) {
int ret, to_copy = dest_vol_info->leb_size;
if (to_copy > bytes)
to_copy = bytes;
ret = read(src_fd, buf, to_copy);
if (ret <= 0) {
if (errno == EINTR) {
warnmsg("do not interrupt me!");
continue;
} else {
errmsg("cannot read %d bytes from \"%s\"",
to_copy, args.srcNode);
goto out_close;
}
}
err = ubi_write(dest_fd, buf, ret);
if (err)
{
errmsg("cannot write %d bytes to \"%s\"", ret, args.destNode);
goto out_close;
}
bytes -= ret;
fprintf(stdout, "\r" PROGRAM_NAME ": copied %lli%%", (100 - (100 * bytes / src_vol_info->data_bytes)));
fflush(stdout);
}
fprintf(stdout, "\r" PROGRAM_NAME ": finished copying ubi%d:%s to ubi%d:%s\n", src_vol_info->dev_num, src_vol_info->name, dest_vol_info->dev_num, dest_vol_info->name);
ret = 0;
out_close:
close(dest_fd);
out_close1:
close(src_fd);
out_free:
free(buf);
return ret;
}
int main(int argc, char * const argv[])
{
int err;
libubi_t libubi;
struct ubi_vol_info src_vol_info, dest_vol_info;
int ret = -1;
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.srcNode);
if (err == 1) {
errmsg("Source node \"%s\" is a UBI device node, not a UBI volume node",
args.srcNode);
goto out_libubi;
} else if (err < 0) {
if (errno == ENODEV)
errmsg("Source node \"%s\" is not a UBI volume node", args.srcNode);
else
errmsg("error while probing source node \"%s\"", args.srcNode);
goto out_libubi;
}
err = ubi_probe_node(libubi, args.destNode);
if (err == 1) {
errmsg("Destination node \"%s\" is a UBI device node, not a UBI volume node",
args.destNode);
goto out_libubi;
} else if (err < 0) {
if (errno == ENODEV)
errmsg("Destination node \"%s\" is not a UBI volume node", args.destNode);
else
errmsg("error while probing destination node \"%s\"", args.destNode);
goto out_libubi;
}
err = ubi_get_vol_info(libubi, args.srcNode, &src_vol_info);
if (err) {
errmsg("cannot get information about source UBI volume \"%s\"",
args.srcNode);
goto out_libubi;
}
err = ubi_get_vol_info(libubi, args.destNode, &dest_vol_info);
if (err) {
errmsg("cannot get information about destination UBI volume \"%s\"",
args.destNode);
goto out_libubi;
}
if ((src_vol_info.dev_num == dest_vol_info.dev_num) && (src_vol_info.vol_id == dest_vol_info.vol_id)) {
errmsg("Source and destination nodes \"%s\" and \"%s\" point to same volume", args.srcNode, args.destNode);
goto out_libubi;
}
err = copy_volume(libubi, &src_vol_info, &dest_vol_info);
if (err)
{
errmsg("copy failed, err=%d", args.srcNode, args.destNode, err);
goto out_libubi;
}
ret = 0;
out_libubi:
libubi_close(libubi);
return ret;
}