X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;ds=sidebyside;f=misc%2Fe2image.c;h=347759b219771335acce728e9c91e37537e543b8;hb=1f4a5aba59f39a33a84152b5ae3ec0a5657b12a1;hp=3cb92fee27763ac56c970c5a15d4ef3faff5a1a3;hpb=2b7a30cc52fd27a1cf479d8682880d11ffd9850f;p=tools%2Fe2fsprogs.git diff --git a/misc/e2image.c b/misc/e2image.c index 3cb92fe..347759b 100644 --- a/misc/e2image.c +++ b/misc/e2image.c @@ -10,8 +10,12 @@ * %End-Header% */ +#ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE +#endif +#ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE +#endif #include "config.h" #include @@ -35,33 +39,49 @@ extern int optind; #include #include #include +#include #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" +#include "ext2fs/ext2fsP.h" #include "et/com_err.h" #include "uuid/uuid.h" #include "e2p/e2p.h" #include "ext2fs/e2image.h" #include "ext2fs/qcow2.h" +#include "support/nls-enable.h" +#include "support/plausible.h" #include "../version.h" -#include "nls-enable.h" - -#define QCOW_OFLAG_COPIED (1LL << 63) - - -const char * program_name = "e2image"; -char * device_name = NULL; - -static void lseek_error_and_exit(int errnum) -{ - perror("seek"); - exit(1); -} -static blk64_t align_offset(blk64_t offset, int n) +#define QCOW_OFLAG_COPIED (1ULL << 63) +#define NO_BLK ((blk64_t) -1) + +/* Image types */ +#define E2IMAGE_RAW 1 +#define E2IMAGE_QCOW2 2 + +/* Image flags */ +#define E2IMAGE_INSTALL_FLAG 1 +#define E2IMAGE_SCRAMBLE_FLAG 2 +#define E2IMAGE_IS_QCOW2_FLAG 4 +#define E2IMAGE_CHECK_ZERO_FLAG 8 + +static const char * program_name = "e2image"; +static char * device_name = NULL; +static char all_data; +static char output_is_blk; +static char nop_flag; +/* writing to blk device: don't skip zeroed blocks */ +static blk64_t source_offset, dest_offset; +static char move_mode; +static char show_progress; +static char *check_buf; +static int skipped_blocks; + +static blk64_t align_offset(blk64_t offset, unsigned int n) { - return (offset + n - 1) & ~(n - 1); + return (offset + n - 1) & ~((blk64_t) n - 1); } static int get_bits_from_size(size_t size) @@ -84,11 +104,62 @@ static int get_bits_from_size(size_t size) static void usage(void) { - fprintf(stderr, _("Usage: %s [-rsIQ] device image_file\n"), + fprintf(stderr, _("Usage: %s [ -r|-Q ] [ -f ] [ -b superblock ] [ -B blocksize ] " + "device image-file\n"), + program_name); + fprintf(stderr, _(" %s -I device image-file\n"), program_name); + fprintf(stderr, _(" %s -ra [ -cfnp ] [ -o src_offset ] " + "[ -O dest_offset ] src_fs [ dest_fs ]\n"), program_name); exit (1); } +static ext2_loff_t seek_relative(int fd, int offset) +{ + ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_CUR); + if (ret < 0) { + perror("seek_relative"); + exit(1); + } + return ret; +} + +static ext2_loff_t seek_set(int fd, ext2_loff_t offset) +{ + ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_SET); + if (ret < 0) { + perror("seek_set"); + exit(1); + } + return ret; +} + +/* + * Returns true if the block we are about to write is identical to + * what is already on the disk. + */ +static int check_block(int fd, void *buf, void *cbuf, int blocksize) +{ + char *cp = cbuf; + int count = blocksize, ret; + + if (cbuf == NULL) + return 0; + + while (count > 0) { + ret = read(fd, cp, count); + if (ret < 0) { + perror("check_block"); + exit(1); + } + count -= ret; + cp += ret; + } + ret = memcmp(buf, cbuf, blocksize); + seek_relative(fd, -blocksize); + return (ret == 0) ? 1 : 0; +} + static void generic_write(int fd, void *buf, int blocksize, blk64_t block) { int count, free_buf = 0; @@ -101,11 +172,17 @@ static void generic_write(int fd, void *buf, int blocksize, blk64_t block) free_buf = 1; err = ext2fs_get_arrayzero(1, blocksize, &buf); if (err) { - com_err(program_name, err, "while allocating buffer"); + com_err(program_name, err, "%s", + _("while allocating buffer")); exit(1); } } - + if (nop_flag) { + printf(_("Writing block %llu\n"), (unsigned long long) block); + if (fd != 1) + seek_relative(fd, blocksize); + goto free_and_return; + } count = write(fd, buf, blocksize); if (count != blocksize) { if (count == -1) @@ -114,13 +191,16 @@ static void generic_write(int fd, void *buf, int blocksize, blk64_t block) err = 0; if (block) - com_err(program_name, err, "error writing block %llu", - block); + com_err(program_name, err, + _("error writing block %llu"), + (unsigned long long) block); else - com_err(program_name, err, "error in write()"); + com_err(program_name, err, "%s", + _("error in generic_write()")); exit(1); } +free_and_return: if (free_buf) ext2fs_free_mem(&buf); } @@ -132,8 +212,8 @@ static void write_header(int fd, void *hdr, int hdr_size, int wrt_size) /* Sanity check */ if (hdr_size > wrt_size) { - fprintf(stderr, _("Error: header size is bigger than " - "wrt_size\n")); + fprintf(stderr, "%s", + _("Error: header size is bigger than wrt_size\n")); } ret = ext2fs_get_mem(wrt_size, &header_buf); @@ -142,16 +222,13 @@ static void write_header(int fd, void *hdr, int hdr_size, int wrt_size) exit(1); } - if (ext2fs_llseek(fd, 0, SEEK_SET) < 0) { - perror("ext2fs_llseek while writing header"); - exit(1); - } + seek_set(fd, 0); memset(header_buf, 0, wrt_size); if (hdr) memcpy(header_buf, hdr, hdr_size); - generic_write(fd, header_buf, wrt_size, 0); + generic_write(fd, header_buf, wrt_size, NO_BLK); ext2fs_free_mem(&header_buf); } @@ -162,64 +239,68 @@ static void write_image_file(ext2_filsys fs, int fd) struct stat st; errcode_t retval; - write_header(fd, NULL, fs->blocksize, fs->blocksize); + write_header(fd, NULL, sizeof(struct ext2_image_hdr), fs->blocksize); memset(&hdr, 0, sizeof(struct ext2_image_hdr)); - hdr.offset_super = ext2fs_llseek(fd, 0, SEEK_CUR); + hdr.offset_super = ext2fs_cpu_to_le32(seek_relative(fd, 0)); retval = ext2fs_image_super_write(fs, fd, 0); if (retval) { - com_err(program_name, retval, _("while writing superblock")); + com_err(program_name, retval, "%s", + _("while writing superblock")); exit(1); } - hdr.offset_inode = ext2fs_llseek(fd, 0, SEEK_CUR); + hdr.offset_inode = ext2fs_cpu_to_le32(seek_relative(fd, 0)); retval = ext2fs_image_inode_write(fs, fd, (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0); if (retval) { - com_err(program_name, retval, _("while writing inode table")); + com_err(program_name, retval, "%s", + _("while writing inode table")); exit(1); } - hdr.offset_blockmap = ext2fs_llseek(fd, 0, SEEK_CUR); + hdr.offset_blockmap = ext2fs_cpu_to_le32(seek_relative(fd, 0)); retval = ext2fs_image_bitmap_write(fs, fd, 0); if (retval) { - com_err(program_name, retval, _("while writing block bitmap")); + com_err(program_name, retval, "%s", + _("while writing block bitmap")); exit(1); } - hdr.offset_inodemap = ext2fs_llseek(fd, 0, SEEK_CUR); + hdr.offset_inodemap = ext2fs_cpu_to_le32(seek_relative(fd, 0)); retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP); if (retval) { - com_err(program_name, retval, _("while writing inode bitmap")); + com_err(program_name, retval, "%s", + _("while writing inode bitmap")); exit(1); } - hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE; + hdr.magic_number = ext2fs_cpu_to_le32(EXT2_ET_MAGIC_E2IMAGE); strcpy(hdr.magic_descriptor, "Ext2 Image 1.0"); gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname)); strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1); hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0; - hdr.fs_blocksize = fs->blocksize; + hdr.fs_blocksize = ext2fs_cpu_to_le32(fs->blocksize); if (stat(device_name, &st) == 0) - hdr.fs_device = st.st_rdev; + hdr.fs_device = ext2fs_cpu_to_le32(st.st_rdev); if (fstat(fd, &st) == 0) { - hdr.image_device = st.st_dev; - hdr.image_inode = st.st_ino; + hdr.image_device = ext2fs_cpu_to_le32(st.st_dev); + hdr.image_inode = ext2fs_cpu_to_le32(st.st_ino); } memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid)); - hdr.image_time = time(0); - write_header(fd, &hdr, fs->blocksize, fs->blocksize); + hdr.image_time = ext2fs_cpu_to_le32(time(0)); + write_header(fd, &hdr, sizeof(struct ext2_image_hdr), fs->blocksize); } /* * These set of functions are used to write a RAW image file. */ -ext2fs_block_bitmap meta_block_map; -ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */ -blk64_t meta_blocks_count; +static ext2fs_block_bitmap meta_block_map; +static ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */ +static blk64_t meta_blocks_count; struct process_block_struct { ext2_ino_t ino; @@ -232,7 +313,7 @@ struct process_block_struct { * structure, so there's no point in letting the ext2fs library read * the inode again. */ -static ino_t stashed_ino = 0; +static ext2_ino_t stashed_ino = 0; static struct ext2_inode *stashed_inode; static errcode_t meta_get_blocks(ext2_filsys fs EXT2FS_ATTR((unused)), @@ -270,9 +351,9 @@ static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)), return 0; } -static void use_inode_shortcuts(ext2_filsys fs, int bool) +static void use_inode_shortcuts(ext2_filsys fs, int use_shortcuts) { - if (bool) { + if (use_shortcuts) { fs->get_blocks = meta_get_blocks; fs->check_directory = meta_check_directory; fs->read_inode = meta_read_inode; @@ -309,7 +390,7 @@ static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data EXT2FS_ATTR((unused))) { - if (blockcnt < 0) { + if (blockcnt < 0 || all_data) { ext2fs_mark_block_bitmap2(meta_block_map, *block_nr); meta_blocks_count++; } @@ -337,22 +418,39 @@ static void mark_table_blocks(ext2_filsys fs) } meta_blocks_count += fs->desc_blocks; + /* + * Mark MMP block + */ + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) { + ext2fs_mark_block_bitmap2(meta_block_map, fs->super->s_mmp_block); + meta_blocks_count++; + } + for (i = 0; i < fs->group_desc_count; i++) { /* * Mark the blocks used for the inode table */ - if (ext2fs_inode_table_loc(fs, i)) { + if ((output_is_blk || + !ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT)) && + ext2fs_inode_table_loc(fs, i)) { + unsigned int end = (unsigned) fs->inode_blocks_per_group; + /* skip unused blocks */ + if (!output_is_blk && ext2fs_has_group_desc_csum(fs)) + end -= (ext2fs_bg_itable_unused(fs, i) / + EXT2_INODES_PER_BLOCK(fs->super)); for (j = 0, b = ext2fs_inode_table_loc(fs, i); - j < (unsigned) fs->inode_blocks_per_group; - j++, b++) + j < end; + j++, b++) { ext2fs_mark_block_bitmap2(meta_block_map, b); - meta_blocks_count += fs->inode_blocks_per_group; + meta_blocks_count++; + } } /* * Mark block used for the block bitmap */ - if (ext2fs_block_bitmap_loc(fs, i)) { + if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && + ext2fs_block_bitmap_loc(fs, i)) { ext2fs_mark_block_bitmap2(meta_block_map, ext2fs_block_bitmap_loc(fs, i)); meta_blocks_count++; @@ -361,7 +459,8 @@ static void mark_table_blocks(ext2_filsys fs) /* * Mark block used for the inode bitmap */ - if (ext2fs_inode_bitmap_loc(fs, i)) { + if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && + ext2fs_inode_bitmap_loc(fs, i)) { ext2fs_mark_block_bitmap2(meta_block_map, ext2fs_inode_bitmap_loc(fs, i)); meta_blocks_count++; @@ -377,6 +476,8 @@ static int check_zero_block(char *buf, int blocksize) char *cp = buf; int left = blocksize; + if (output_is_blk) + return 0; while (left > 0) { if (*cp++) return 0; @@ -385,20 +486,7 @@ static int check_zero_block(char *buf, int blocksize) return 1; } -static void write_block(int fd, char *buf, int sparse_offset, - int blocksize, blk64_t block) -{ - ext2_loff_t ret = 0; - - if (sparse_offset) - ret = ext2fs_llseek(fd, sparse_offset, SEEK_CUR); - - if (ret < 0) - lseek_error_and_exit(errno); - generic_write(fd, buf, blocksize, block); -} - -int name_id[256]; +static int name_id[256]; #define EXT4_MAX_REC_LEN ((1<<16)-1) @@ -425,9 +513,9 @@ static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf) #endif if (rec_len < 8 || (rec_len % 4) || (p+rec_len > end)) { - printf("Corrupt directory block %lu: " - "bad rec_len (%d)\n", (unsigned long) blk, - rec_len); + printf(_("Corrupt directory block %llu: " + "bad rec_len (%d)\n"), + (unsigned long long) blk, rec_len); rec_len = end - p; (void) ext2fs_set_rec_len(fs, rec_len, (struct ext2_dir_entry *) dirent); @@ -436,10 +524,10 @@ static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf) #endif continue; } - if (dirent->name_len + 8 > rec_len) { - printf("Corrupt directory block %lu: " - "bad name_len (%d)\n", (unsigned long) blk, - dirent->name_len); + if (dirent->name_len + 8U > rec_len) { + printf(_("Corrupt directory block %llu: " + "bad name_len (%d)\n"), + (unsigned long long) blk, dirent->name_len); dirent->name_len = rec_len - 8; continue; } @@ -464,67 +552,225 @@ static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf) } } -static void output_meta_data_blocks(ext2_filsys fs, int fd) +static char got_sigint; + +static void sigint_handler(int unused EXT2FS_ATTR((unused))) +{ + got_sigint = 1; + signal (SIGINT, SIG_DFL); +} + +#define calc_percent(a, b) ((int) ((100.0 * (((float) (a)) / \ + ((float) (b)))) + 0.5)) +#define calc_rate(t, b, d) (((float)(t) / ((float)(1024 * 1024) / (b))) / (d)) + +static int print_progress(blk64_t num, blk64_t total) +{ + return fprintf(stderr, _("%llu / %llu blocks (%d%%)"), + (unsigned long long) num, + (unsigned long long) total, + calc_percent(num, total)); +} + +static void output_meta_data_blocks(ext2_filsys fs, int fd, int flags) { errcode_t retval; blk64_t blk; char *buf, *zero_buf; int sparse = 0; + blk64_t start = 0; + blk64_t distance = 0; + blk64_t end = ext2fs_blocks_count(fs->super); + time_t last_update = 0; + time_t start_time = 0; + blk64_t total_written = 0; + int bscount = 0; retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) { - com_err(program_name, retval, "while allocating buffer"); + com_err(program_name, retval, "%s", + _("while allocating buffer")); exit(1); } retval = ext2fs_get_memzero(fs->blocksize, &zero_buf); if (retval) { - com_err(program_name, retval, "while allocating buffer"); + com_err(program_name, retval, "%s", + _("while allocating buffer")); exit(1); } - for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) { + if (show_progress) { + fprintf(stderr, "%s", _("Copying ")); + bscount = print_progress(total_written, meta_blocks_count); + fflush(stderr); + last_update = time(NULL); + start_time = time(NULL); + } + /* when doing an in place move to the right, you can't start + at the beginning or you will overwrite data, so instead + divide the fs up into distance size chunks and write them + in reverse. */ + if (move_mode && dest_offset > source_offset) { + distance = (dest_offset - source_offset) / fs->blocksize; + if (distance < ext2fs_blocks_count(fs->super)) + start = ext2fs_blocks_count(fs->super) - distance; + } + if (move_mode) + signal (SIGINT, sigint_handler); +more_blocks: + if (distance) + seek_set(fd, (start * fs->blocksize) + dest_offset); + for (blk = start; blk < end; blk++) { + if (got_sigint) { + if (distance) { + /* moving to the right */ + if (distance >= ext2fs_blocks_count(fs->super)|| + start == ext2fs_blocks_count(fs->super) - + distance) + kill(getpid(), SIGINT); + } else { + /* moving to the left */ + if (blk < (source_offset - dest_offset) / + fs->blocksize) + kill(getpid(), SIGINT); + } + if (show_progress) + fputc('\r', stderr); + fprintf(stderr, "%s", + _("Stopping now will destroy the filesystem, " + "interrupt again if you are sure\n")); + if (show_progress) { + fprintf(stderr, "%s", _("Copying ")); + bscount = print_progress(total_written, + meta_blocks_count); + fflush(stderr); + } + + got_sigint = 0; + } + if (show_progress && last_update != time(NULL)) { + time_t duration; + last_update = time(NULL); + while (bscount--) + fputc('\b', stderr); + bscount = print_progress(total_written, + meta_blocks_count); + duration = time(NULL) - start_time; + if (duration > 5 && total_written) { + time_t est = (duration * meta_blocks_count / + total_written) - duration; + char buff[30]; + strftime(buff, 30, "%T", gmtime(&est)); + bscount += + fprintf(stderr, + _(" %s remaining at %.2f MB/s"), + buff, calc_rate(total_written, + fs->blocksize, + duration)); + } + fflush (stderr); + } if ((blk >= fs->super->s_first_data_block) && ext2fs_test_block_bitmap2(meta_block_map, blk)) { retval = io_channel_read_blk64(fs->io, blk, 1, buf); if (retval) { com_err(program_name, retval, - "error reading block %llu", blk); + _("error reading block %llu"), + (unsigned long long) blk); } + total_written++; if (scramble_block_map && ext2fs_test_block_bitmap2(scramble_block_map, blk)) scramble_dir_block(fs, blk, buf); - if ((fd != 1) && check_zero_block(buf, fs->blocksize)) + if ((flags & E2IMAGE_CHECK_ZERO_FLAG) && + check_zero_block(buf, fs->blocksize)) goto sparse_write; - write_block(fd, buf, sparse, fs->blocksize, blk); + if (sparse) + seek_relative(fd, sparse); sparse = 0; + if (check_block(fd, buf, check_buf, fs->blocksize)) { + seek_relative(fd, fs->blocksize); + skipped_blocks++; + } else + generic_write(fd, buf, fs->blocksize, blk); } else { sparse_write: if (fd == 1) { - write_block(fd, zero_buf, 0, - fs->blocksize, blk); + if (!nop_flag) + generic_write(fd, zero_buf, + fs->blocksize, blk); continue; } sparse += fs->blocksize; if (sparse > 1024*1024) { - write_block(fd, 0, 1024*1024, 0, 0); + seek_relative(fd, 1024*1024); sparse -= 1024*1024; } } } - if (sparse) - write_block(fd, zero_buf, sparse-1, 1, -1); + if (distance && start) { + if (start < distance) { + end = start; + start = 0; + } else { + end -= distance; + start -= distance; + if (end < distance) { + /* past overlap, do rest in one go */ + end = start; + start = 0; + } + } + sparse = 0; + goto more_blocks; + } + signal (SIGINT, SIG_DFL); + if (show_progress) { + time_t duration = time(NULL) - start_time; + char buff[30]; + fputc('\r', stderr); + strftime(buff, 30, "%T", gmtime(&duration)); + fprintf(stderr, _("Copied %llu / %llu blocks (%d%%) in %s "), + (unsigned long long) total_written, + (unsigned long long) meta_blocks_count, + calc_percent(total_written, meta_blocks_count), buff); + if (duration) + fprintf(stderr, _("at %.2f MB/s"), + calc_rate(total_written, fs->blocksize, duration)); + fputs(" \n", stderr); + } +#ifdef HAVE_FTRUNCATE64 + if (sparse) { + ext2_loff_t offset; + if (distance) + offset = seek_set(fd, + fs->blocksize * ext2fs_blocks_count(fs->super) + dest_offset); + else + offset = seek_relative(fd, sparse); + + if (ftruncate64(fd, offset) < 0) { + seek_relative(fd, -1); + generic_write(fd, zero_buf, 1, NO_BLK); + } + } +#else + if (sparse && !distance) { + seek_relative(fd, sparse-1); + generic_write(fd, zero_buf, 1, NO_BLK); + } +#endif ext2fs_free_mem(&zero_buf); ext2fs_free_mem(&buf); } -static void init_l1_table(struct ext2_super_block *sb, - struct ext2_qcow2_image *image) +static void init_l1_table(struct ext2_qcow2_image *image) { __u64 *l1_table; errcode_t ret; ret = ext2fs_get_arrayzero(image->l1_size, sizeof(__u64), &l1_table); if (ret) { - com_err(program_name, ret, "while allocating l1 table"); + com_err(program_name, ret, "%s", + _("while allocating l1 table")); exit(1); } @@ -569,7 +815,7 @@ static void init_l2_cache(struct ext2_qcow2_image *image) return; alloc_err: - com_err(program_name, ret, "while allocating l2 cache"); + com_err(program_name, ret, "%s", _("while allocating l2 cache")); exit(1); } @@ -592,9 +838,10 @@ again: } if (cache->free != cache->count) { - fprintf(stderr, "Warning: There are still tables in the " - "cache while putting the cache, data will " - "be lost so the image may not be valid.\n"); + fprintf(stderr, "%s", _("Warning: There are still tables in " + "the cache while putting the cache, " + "data will be lost so the image may " + "not be valid.\n")); table = cache->used_head; cache->used_head = NULL; goto again; @@ -641,8 +888,8 @@ static int init_refcount(struct ext2_qcow2_image *img, blk64_t table_offset) return ret; } -static int initialize_qcow2_image(int fd, ext2_filsys fs, - struct ext2_qcow2_image *image) +static errcode_t initialize_qcow2_image(int fd, ext2_filsys fs, + struct ext2_qcow2_image *image) { struct ext2_qcow2_hdr *header; blk64_t total_size, offset; @@ -650,6 +897,10 @@ static int initialize_qcow2_image(int fd, ext2_filsys fs, int cluster_bits = get_bits_from_size(fs->blocksize); struct ext2_super_block *sb = fs->super; + /* Sbould never happen, but just in case... */ + if (cluster_bits < 0) + return EXT2_FILSYS_CORRUPTED; + /* Allocate header */ ret = ext2fs_get_memzero(sizeof(struct ext2_qcow2_hdr), &header); if (ret) @@ -703,7 +954,7 @@ static int initialize_qcow2_image(int fd, ext2_filsys fs, image->hdr = header; /* Initialize l1 and l2 tables */ - init_l1_table(sb, image); + init_l1_table(image); init_l2_cache(image); return 0; @@ -766,25 +1017,23 @@ static void flush_l2_cache(struct ext2_qcow2_image *image) int fd = image->fd; /* Store current position */ - if ((offset = ext2fs_llseek(fd, 0, SEEK_CUR)) < 0) - lseek_error_and_exit(errno); + offset = seek_relative(fd, 0); assert(table); while (cache->free < cache->count) { if (seek != table->offset) { - if (ext2fs_llseek(fd, table->offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, table->offset); seek = table->offset; } - generic_write(fd, (char *)table->data, image->cluster_size , 0); + generic_write(fd, (char *)table->data, image->cluster_size, + NO_BLK); put_used_table(image, &table); seek += image->cluster_size; } /* Restore previous position */ - if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, offset); } /** @@ -861,11 +1110,10 @@ static int update_refcount(int fd, struct ext2_qcow2_image *img, */ if (table_index != ref->refcount_table_index) { - if (ext2fs_llseek(fd, ref->refcount_block_offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, ref->refcount_block_offset); generic_write(fd, (char *)ref->refcount_block, - img->cluster_size, 0); + img->cluster_size, NO_BLK); memset(ref->refcount_block, 0, img->cluster_size); ref->refcount_table[ref->refcount_table_index] = @@ -879,7 +1127,7 @@ static int update_refcount(int fd, struct ext2_qcow2_image *img, /* * We are relying on the fact that we are creating the qcow2 * image sequentially, hence we will always allocate refcount - * block items sequentialy. + * block items sequentially. */ ref->refcount_block[ref->refcount_block_index] = ext2fs_cpu_to_be16(1); ref->refcount_block_index++; @@ -894,14 +1142,13 @@ static int sync_refcount(int fd, struct ext2_qcow2_image *img) ref->refcount_table[ref->refcount_table_index] = ext2fs_cpu_to_be64(ref->refcount_block_offset); - if (ext2fs_llseek(fd, ref->refcount_table_offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, ref->refcount_table_offset); generic_write(fd, (char *)ref->refcount_table, - ref->refcount_table_clusters << img->cluster_bits, 0); + ref->refcount_table_clusters << img->cluster_bits, NO_BLK); - if (ext2fs_llseek(fd, ref->refcount_block_offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); - generic_write(fd, (char *)ref->refcount_block, img->cluster_size, 0); + seek_set(fd, ref->refcount_block_offset); + generic_write(fd, (char *)ref->refcount_block, img->cluster_size, + NO_BLK); return 0; } @@ -916,15 +1163,15 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) /* allocate struct ext2_qcow2_image */ retval = ext2fs_get_mem(sizeof(struct ext2_qcow2_image), &img); if (retval) { - com_err(program_name, retval, - "while allocating ext2_qcow2_image"); + com_err(program_name, retval, "%s", + _("while allocating ext2_qcow2_image")); exit(1); } retval = initialize_qcow2_image(fd, fs, img); if (retval) { - com_err(program_name, retval, - "while initializing ext2_qcow2_image"); + com_err(program_name, retval, "%s", + _("while initializing ext2_qcow2_image")); exit(1); } header_size = align_offset(sizeof(struct ext2_qcow2_hdr), @@ -933,8 +1180,7 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) /* Refcount all qcow2 related metadata up to refcount_block_offset */ end = img->refcount.refcount_block_offset; - if (ext2fs_llseek(fd, end, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, end); blk = end + img->cluster_size; for (offset = 0; offset <= end; offset += img->cluster_size) { if (update_refcount(fd, img, offset, blk)) { @@ -946,12 +1192,12 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) end += img->cluster_size; } } - if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, offset); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) { - com_err(program_name, retval, "while allocating buffer"); + com_err(program_name, retval, "%s", + _("while allocating buffer")); exit(1); } /* Write qcow2 data blocks */ @@ -961,7 +1207,8 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) retval = io_channel_read_blk64(fs->io, blk, 1, buf); if (retval) { com_err(program_name, retval, - "error reading block %llu", blk); + _("error reading block %llu"), + (unsigned long long) blk); continue; } if (scramble_block_map && @@ -973,8 +1220,7 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) if (update_refcount(fd, img, offset, offset)) { /* Make space for another refcount block */ offset += img->cluster_size; - if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, offset); /* * We have created the new refcount block, this * means that we need to refcount it as well. @@ -984,14 +1230,15 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) * block should not be created! */ if (update_refcount(fd, img, offset, offset)) { - fprintf(stderr, "Programming error: " - "multiple sequential refcount " - "blocks created!\n"); + fprintf(stderr, "%s", + _("Programming error: multiple " + "sequential refcount blocks " + "created!\n")); exit(1); } } - generic_write(fd, buf, fs->blocksize, 0); + generic_write(fd, buf, fs->blocksize, blk); if (add_l2_item(img, blk, offset, offset + img->cluster_size)) { @@ -1001,15 +1248,14 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) offset += img->cluster_size; if (update_refcount(fd, img, offset, offset)) { - fprintf(stderr, - "Programming error: multiple sequential refcount " - "blocks created!\n"); + fprintf(stderr, "%s", + _("Programming error: multiple sequential refcount " + "blocks created!\n")); exit(1); } } offset += img->cluster_size; - if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, offset); continue; } @@ -1021,16 +1267,16 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) sync_refcount(fd, img); /* Write l1_table*/ - if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) - lseek_error_and_exit(errno); + seek_set(fd, img->l1_offset); size = img->l1_size * sizeof(__u64); - generic_write(fd, (char *)img->l1_table, size, 0); + generic_write(fd, (char *)img->l1_table, size, NO_BLK); ext2fs_free_mem(&buf); free_qcow2_image(img); } -static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) +static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags, + blk64_t superblock) { struct process_block_struct pb; struct ext2_inode inode; @@ -1040,10 +1286,11 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) char * block_buf; meta_blocks_count = 0; - retval = ext2fs_allocate_block_bitmap(fs, "in-use block map", + retval = ext2fs_allocate_block_bitmap(fs, _("in-use block map"), &meta_block_map); if (retval) { - com_err(program_name, retval, "while allocating block bitmap"); + com_err(program_name, retval, "%s", + _("while allocating block bitmap")); exit(1); } @@ -1051,23 +1298,43 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) retval = ext2fs_allocate_block_bitmap(fs, "scramble block map", &scramble_block_map); if (retval) { - com_err(program_name, retval, - "while allocating scramble block bitmap"); + com_err(program_name, retval, "%s", + _("while allocating scramble block bitmap")); exit(1); } } + if (superblock) { + int j; + + ext2fs_mark_block_bitmap2(meta_block_map, superblock); + meta_blocks_count++; + + /* + * Mark the backup superblock descriptors + */ + for (j = 0; j < fs->desc_blocks; j++) { + ext2fs_mark_block_bitmap2(meta_block_map, + ext2fs_descriptor_block_loc2(fs, superblock, j)); + } + meta_blocks_count += fs->desc_blocks; + } + mark_table_blocks(fs); + if (show_progress) + fprintf(stderr, "%s", _("Scanning inodes...\n")); retval = ext2fs_open_inode_scan(fs, 0, &scan); if (retval) { - com_err(program_name, retval, _("while opening inode scan")); + com_err(program_name, retval, "%s", + _("while opening inode scan")); exit(1); } retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf); if (retval) { - com_err(program_name, 0, "Can't allocate block buffer"); + com_err(program_name, 0, "%s", + _("Can't allocate block buffer")); exit(1); } @@ -1078,7 +1345,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) continue; if (retval) { - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("while getting next inode")); exit(1); } @@ -1106,7 +1373,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) process_dir_block, &pb); if (retval) { com_err(program_name, retval, - "while iterating over inode %u", + _("while iterating over inode %u"), ino); exit(1); } @@ -1114,13 +1381,13 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) if ((inode.i_flags & EXT4_EXTENTS_FL) || inode.i_block[EXT2_IND_BLOCK] || inode.i_block[EXT2_DIND_BLOCK] || - inode.i_block[EXT2_TIND_BLOCK]) { + inode.i_block[EXT2_TIND_BLOCK] || all_data) { retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, block_buf, process_file_block, &pb); if (retval) { com_err(program_name, retval, - "while iterating over inode %u", ino); + _("while iterating over inode %u"), ino); exit(1); } } @@ -1131,7 +1398,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) if (type & E2IMAGE_QCOW2) output_qcow2_meta_data_blocks(fs, fd); else - output_meta_data_blocks(fs, fd); + output_meta_data_blocks(fs, fd, flags); ext2fs_free_mem(&block_buf); ext2fs_close_inode_scan(scan); @@ -1144,14 +1411,15 @@ static void install_image(char *device, char *image_fn, int type) { errcode_t retval; ext2_filsys fs; - int open_flag = EXT2_FLAG_IMAGE_FILE; + int open_flag = EXT2_FLAG_IMAGE_FILE | EXT2_FLAG_64BITS | + EXT2_FLAG_IGNORE_CSUM_ERRORS; int fd = 0; io_manager io_ptr; io_channel io; if (type) { - com_err(program_name, 0, "Raw and qcow2 images cannot" - "be installed"); + com_err(program_name, 0, "%s", + _("Raw and qcow2 images cannot be installed")); exit(1); } @@ -1166,14 +1434,14 @@ static void install_image(char *device, char *image_fn, int type) retval = ext2fs_open (image_fn, open_flag, 0, 0, io_ptr, &fs); if (retval) { - com_err (program_name, retval, _("while trying to open %s"), - image_fn); + com_err(program_name, retval, _("while trying to open %s"), + image_fn); exit(1); } retval = ext2fs_read_bitmaps (fs); if (retval) { - com_err(program_name, retval, "error reading bitmaps"); + com_err(program_name, retval, "%s", _("error reading bitmaps")); exit(1); } @@ -1185,25 +1453,23 @@ static void install_image(char *device, char *image_fn, int type) retval = io_ptr->open(device, IO_FLAG_RW, &io); if (retval) { - com_err(device, 0, "while opening device file"); + com_err(device, 0, "%s", _("while opening device file")); exit(1); } ext2fs_rewrite_to_io(fs, io); - if (ext2fs_llseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) { - perror("ext2fs_llseek"); - exit(1); - } + seek_set(fd, ext2fs_le32_to_cpu(fs->image_header->offset_inode)); retval = ext2fs_image_inode_read(fs, fd, 0); if (retval) { - com_err(image_fn, 0, "while restoring the image table"); + com_err(image_fn, 0, "%s", + _("while restoring the image table")); exit(1); } - ext2fs_close (fs); - exit (0); + close(fd); + ext2fs_close_free(&fs); } static struct ext2_qcow2_hdr *check_qcow2_image(int *fd, char *name) @@ -1221,14 +1487,21 @@ int main (int argc, char ** argv) int c; errcode_t retval; ext2_filsys fs; - char *image_fn; + char *image_fn, offset_opt[64]; struct ext2_qcow2_hdr *header = NULL; - int open_flag = EXT2_FLAG_64BITS; + int open_flag = EXT2_FLAG_64BITS | EXT2_FLAG_THREADS | + EXT2_FLAG_IGNORE_CSUM_ERRORS; int img_type = 0; int flags = 0; + int mount_flags = 0; int qcow2_fd = 0; int fd = 0; int ret = 0; + int ignore_rw_mount = 0; + int check = 0; + struct stat st; + blk64_t superblock = 0; + int blocksize = 0; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); @@ -1242,8 +1515,14 @@ int main (int argc, char ** argv) if (argc && *argv) program_name = *argv; add_error_table(&et_ext2_error_table); - while ((c = getopt(argc, argv, "rsIQ")) != EOF) + while ((c = getopt(argc, argv, "b:B:nrsIQafo:O:pc")) != EOF) switch (c) { + case 'b': + superblock = strtoull(optarg, NULL, 0); + break; + case 'B': + blocksize = strtoul(optarg, NULL, 0); + break; case 'I': flags |= E2IMAGE_INSTALL_FLAG; break; @@ -1260,13 +1539,82 @@ int main (int argc, char ** argv) case 's': flags |= E2IMAGE_SCRAMBLE_FLAG; break; + case 'a': + all_data = 1; + break; + case 'f': + ignore_rw_mount = 1; + break; + case 'n': + nop_flag = 1; + break; + case 'o': + source_offset = strtoull(optarg, NULL, 0); + break; + case 'O': + dest_offset = strtoull(optarg, NULL, 0); + break; + case 'p': + show_progress = 1; + break; + case 'c': + check = 1; + break; default: usage(); } - if (optind != argc - 2 ) + if (optind == argc - 1 && + (source_offset || dest_offset)) + move_mode = 1; + else if (optind != argc - 2 ) usage(); + + if (all_data && !img_type) { + com_err(program_name, 0, "%s", _("-a option can only be used " + "with raw or QCOW2 images.")); + exit(1); + } + if (superblock && !img_type) { + com_err(program_name, 0, "%s", _("-b option can only be used " + "with raw or QCOW2 images.")); + exit(1); + } + if ((source_offset || dest_offset) && img_type != E2IMAGE_RAW) { + com_err(program_name, 0, "%s", + _("Offsets are only allowed with raw images.")); + exit(1); + } + if (move_mode && img_type != E2IMAGE_RAW) { + com_err(program_name, 0, "%s", + _("Move mode is only allowed with raw images.")); + exit(1); + } + if (move_mode && !all_data) { + com_err(program_name, 0, "%s", + _("Move mode requires all data mode.")); + exit(1); + } device_name = argv[optind]; - image_fn = argv[optind+1]; + if (move_mode) + image_fn = device_name; + else image_fn = argv[optind+1]; + + retval = ext2fs_check_if_mounted(device_name, &mount_flags); + if (retval) { + com_err(program_name, retval, "%s", _("checking if mounted")); + exit(1); + } + + if (img_type && !ignore_rw_mount && + (mount_flags & EXT2_MF_MOUNTED) && + !(mount_flags & EXT2_MF_READONLY)) { + fprintf(stderr, "%s", _("\nRunning e2image on a R/W mounted " + "filesystem can result in an\n" + "inconsistent image which will not be useful " + "for debugging purposes.\n" + "Use -f option if you really want to do that.\n")); + exit(1); + } if (flags & E2IMAGE_INSTALL_FLAG) { install_image(device_name, image_fn, img_type); @@ -1280,13 +1628,15 @@ int main (int argc, char ** argv) goto skip_device; } } - - retval = ext2fs_open (device_name, open_flag, 0, 0, - unix_io_manager, &fs); + sprintf(offset_opt, "offset=%llu", (unsigned long long) source_offset); + retval = ext2fs_open2(device_name, offset_opt, open_flag, + superblock, blocksize, unix_io_manager, &fs); if (retval) { com_err (program_name, retval, _("while trying to open %s"), device_name); fputs(_("Couldn't find valid filesystem superblock.\n"), stdout); + if (retval == EXT2_ET_BAD_MAGIC) + check_plausibility(device_name, CHECK_FS_EXIST, NULL); exit(1); } @@ -1294,44 +1644,91 @@ skip_device: if (strcmp(image_fn, "-") == 0) fd = 1; else { - fd = ext2fs_open_file(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600); + int o_flags = O_CREAT|O_RDWR; + + if (img_type != E2IMAGE_RAW) + o_flags |= O_TRUNC; + if (access(image_fn, F_OK) != 0) + flags |= E2IMAGE_CHECK_ZERO_FLAG; + fd = ext2fs_open_file(image_fn, o_flags, 0600); if (fd < 0) { com_err(program_name, errno, - _("while trying to open %s"), argv[optind+1]); + _("while trying to open %s"), image_fn); exit(1); } } + if (dest_offset) + seek_set(fd, dest_offset); if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) { - com_err(program_name, 0, "QCOW2 image can not be written to " - "the stdout!\n"); + com_err(program_name, 0, "%s", + _("QCOW2 image can not be written to the stdout!\n")); exit(1); } - + if (fd != 1) { + if (fstat(fd, &st)) { + com_err(program_name, 0, "%s", + _("Can not stat output\n")); + exit(1); + } + if (ext2fsP_is_disk_device(st.st_mode)) + output_is_blk = 1; + } if (flags & E2IMAGE_IS_QCOW2_FLAG) { ret = qcow2_write_raw_image(qcow2_fd, fd, header); if (ret) { if (ret == -QCOW_COMPRESSED) - fprintf(stderr, "Image (%s) is compressed\n", + fprintf(stderr, _("Image (%s) is compressed\n"), + image_fn); + else if (ret == -QCOW_ENCRYPTED) + fprintf(stderr, _("Image (%s) is encrypted\n"), image_fn); - if (ret == -QCOW_ENCRYPTED) - fprintf(stderr, "Image (%s) is encrypted\n", + else if (ret == -QCOW_CORRUPTED) + fprintf(stderr, _("Image (%s) is corrupted\n"), image_fn); - com_err(program_name, ret, - _("while trying to convert qcow2 image" - " (%s) into raw image (%s)"), - device_name, image_fn); + else + com_err(program_name, ret, + _("while trying to convert qcow2 image" + " (%s) into raw image (%s)"), + image_fn, device_name); + ret = 1; } goto out; } - + if (check) { + if (img_type != E2IMAGE_RAW) { + fprintf(stderr, "%s", _("The -c option only supported " + "in raw mode\n")); + exit(1); + } + if (fd == 1) { + fprintf(stderr, "%s", _("The -c option not supported " + "when writing to stdout\n")); + exit(1); + } + retval = ext2fs_get_mem(fs->blocksize, &check_buf); + if (retval) { + com_err(program_name, retval, "%s", + _("while allocating check_buf")); + exit(1); + } + } + if (show_progress && (img_type != E2IMAGE_RAW)) { + fprintf(stderr, "%s", + _("The -p option only supported in raw mode\n")); + exit(1); + } if (img_type) - write_raw_image_file(fs, fd, img_type, flags); + write_raw_image_file(fs, fd, img_type, flags, superblock); else write_image_file(fs, fd); - ext2fs_close (fs); + ext2fs_close_free(&fs); + if (check) + printf(_("%d blocks already contained the data to be copied\n"), + skipped_blocks); + out: if (header) free(header);