| /* |
| * 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; |
| } |