blob: e507cd526e10e8664997e6672f5909f1d1a507a5 [file] [log] [blame]
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001/*
2 * e2image.c --- Program which writes an image file backing up
3 * critical metadata for the filesystem.
4 *
Theodore Ts'oa3827952001-05-03 16:24:57 +00005 * Copyright 2000, 2001 by Theodore Ts'o.
Theodore Ts'o72ed1262000-11-12 19:32:20 +00006 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
Theodore Ts'o6304baf2001-08-09 05:41:29 -040013#define _LARGEFILE_SOURCE
14#define _LARGEFILE64_SOURCE
15
Theodore Ts'o72ed1262000-11-12 19:32:20 +000016#include <fcntl.h>
17#include <grp.h>
18#ifdef HAVE_GETOPT_H
19#include <getopt.h>
20#else
21extern char *optarg;
22extern int optind;
23#endif
24#include <pwd.h>
25#include <stdio.h>
Theodore Ts'of38cf3c2008-09-01 11:17:29 -040026#ifdef HAVE_STDLIB_H
Theodore Ts'o72ed1262000-11-12 19:32:20 +000027#include <stdlib.h>
Theodore Ts'of38cf3c2008-09-01 11:17:29 -040028#endif
Theodore Ts'o72ed1262000-11-12 19:32:20 +000029#include <string.h>
30#include <time.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <sys/stat.h>
35#include <sys/types.h>
JP Abgralle0ed7402014-03-19 19:08:39 -070036#include <assert.h>
37#include <signal.h>
Theodore Ts'o72ed1262000-11-12 19:32:20 +000038
Theodore Ts'o54c637d2001-05-14 11:45:38 +000039#include "ext2fs/ext2_fs.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000040#include "ext2fs/ext2fs.h"
41#include "et/com_err.h"
42#include "uuid/uuid.h"
43#include "e2p/e2p.h"
44#include "ext2fs/e2image.h"
JP Abgralle0ed7402014-03-19 19:08:39 -070045#include "ext2fs/qcow2.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000046
47#include "../version.h"
48#include "nls-enable.h"
49
JP Abgralle0ed7402014-03-19 19:08:39 -070050#define QCOW_OFLAG_COPIED (1LL << 63)
51#define NO_BLK ((blk64_t) -1)
52
53/* Image types */
54#define E2IMAGE_RAW 1
55#define E2IMAGE_QCOW2 2
56
57/* Image flags */
58#define E2IMAGE_INSTALL_FLAG 1
59#define E2IMAGE_SCRAMBLE_FLAG 2
60#define E2IMAGE_IS_QCOW2_FLAG 4
61#define E2IMAGE_CHECK_ZERO_FLAG 8
62
63static const char * program_name = "e2image";
64static char * device_name = NULL;
65static char all_data;
66static char output_is_blk;
67static char nop_flag;
68/* writing to blk device: don't skip zeroed blocks */
69static blk64_t source_offset, dest_offset;
70static char move_mode;
71static char show_progress;
72static char *check_buf;
73static int skipped_blocks;
74
75static blk64_t align_offset(blk64_t offset, unsigned int n)
76{
77 return (offset + n - 1) & ~((blk64_t) n - 1);
78}
79
80static int get_bits_from_size(size_t size)
81{
82 int res = 0;
83
84 if (size == 0)
85 return -1;
86
87 while (size != 1) {
88 /* Not a power of two */
89 if (size & 1)
90 return -1;
91
92 size >>= 1;
93 res++;
94 }
95 return res;
96}
Lukas Czernerbf0449b2011-05-18 13:36:53 +020097
Theodore Ts'o72ed1262000-11-12 19:32:20 +000098static void usage(void)
99{
JP Abgralle0ed7402014-03-19 19:08:39 -0700100 fprintf(stderr, _("Usage: %s [ -r|Q ] [ -fr ] device image-file\n"),
101 program_name);
102 fprintf(stderr, _(" %s -I device image-file\n"), program_name);
103 fprintf(stderr, _(" %s -ra [ -cfnp ] [ -o src_offset ] "
104 "[ -O dest_offset ] src_fs [ dest_fs ]\n"),
Theodore Ts'od851ed32005-01-19 00:26:43 -0500105 program_name);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000106 exit (1);
107}
108
JP Abgralle0ed7402014-03-19 19:08:39 -0700109static ext2_loff_t seek_relative(int fd, int offset)
110{
111 ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_CUR);
112 if (ret < 0) {
113 perror("seek_relative");
114 exit(1);
115 }
116 return ret;
117}
118
119static ext2_loff_t seek_set(int fd, ext2_loff_t offset)
120{
121 ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_SET);
122 if (ret < 0) {
123 perror("seek_set");
124 exit(1);
125 }
126 return ret;
127}
128
129/*
130 * Returns true if the block we are about to write is identical to
131 * what is already on the disk.
132 */
133static int check_block(int fd, void *buf, void *cbuf, int blocksize)
134{
135 char *cp = cbuf;
136 int count = blocksize, ret;
137
138 if (cbuf == NULL)
139 return 0;
140
141 while (count > 0) {
142 ret = read(fd, cp, count);
143 if (ret < 0) {
144 perror("check_block");
145 exit(1);
146 }
147 count -= ret;
148 cp += ret;
149 }
150 ret = memcmp(buf, cbuf, blocksize);
151 seek_relative(fd, -blocksize);
152 return (ret == 0) ? 1 : 0;
153}
154
155static void generic_write(int fd, void *buf, int blocksize, blk64_t block)
156{
157 int count, free_buf = 0;
158 errcode_t err;
159
160 if (!blocksize)
161 return;
162
163 if (!buf) {
164 free_buf = 1;
165 err = ext2fs_get_arrayzero(1, blocksize, &buf);
166 if (err) {
167 com_err(program_name, err,
168 _("while allocating buffer"));
169 exit(1);
170 }
171 }
172 if (nop_flag) {
173 printf(_("Writing block %llu\n"), (unsigned long long) block);
174 if (fd != 1)
175 seek_relative(fd, blocksize);
176 return;
177 }
178 count = write(fd, buf, blocksize);
179 if (count != blocksize) {
180 if (count == -1)
181 err = errno;
182 else
183 err = 0;
184
185 if (block)
186 com_err(program_name, err,
187 _("error writing block %llu"), block);
188 else
189 com_err(program_name, err, _("error in write()"));
190
191 exit(1);
192 }
193 if (free_buf)
194 ext2fs_free_mem(&buf);
195}
196
197static void write_header(int fd, void *hdr, int hdr_size, int wrt_size)
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000198{
Theodore Ts'o095b4592001-05-03 13:33:11 +0000199 char *header_buf;
JP Abgralle0ed7402014-03-19 19:08:39 -0700200 int ret;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000201
JP Abgralle0ed7402014-03-19 19:08:39 -0700202 /* Sanity check */
203 if (hdr_size > wrt_size) {
204 fprintf(stderr, "%s",
205 _("Error: header size is bigger than wrt_size\n"));
206 }
207
208 ret = ext2fs_get_mem(wrt_size, &header_buf);
209 if (ret) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500210 fputs(_("Couldn't allocate header buffer\n"), stderr);
Theodore Ts'o095b4592001-05-03 13:33:11 +0000211 exit(1);
212 }
213
JP Abgralle0ed7402014-03-19 19:08:39 -0700214 seek_set(fd, 0);
215 memset(header_buf, 0, wrt_size);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400216
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000217 if (hdr)
JP Abgralle0ed7402014-03-19 19:08:39 -0700218 memcpy(header_buf, hdr, hdr_size);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400219
JP Abgralle0ed7402014-03-19 19:08:39 -0700220 generic_write(fd, header_buf, wrt_size, NO_BLK);
221
222 ext2fs_free_mem(&header_buf);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000223}
224
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400225static void write_image_file(ext2_filsys fs, int fd)
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000226{
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400227 struct ext2_image_hdr hdr;
228 struct stat st;
229 errcode_t retval;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000230
JP Abgralle0ed7402014-03-19 19:08:39 -0700231 write_header(fd, NULL, sizeof(struct ext2_image_hdr), fs->blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000232 memset(&hdr, 0, sizeof(struct ext2_image_hdr));
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400233
JP Abgralle0ed7402014-03-19 19:08:39 -0700234 hdr.offset_super = seek_relative(fd, 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000235 retval = ext2fs_image_super_write(fs, fd, 0);
236 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700237 com_err(program_name, retval, "%s",
238 _("while writing superblock"));
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000239 exit(1);
240 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400241
JP Abgralle0ed7402014-03-19 19:08:39 -0700242 hdr.offset_inode = seek_relative(fd, 0);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400243 retval = ext2fs_image_inode_write(fs, fd,
244 (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000245 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700246 com_err(program_name, retval, "%s",
247 _("while writing inode table"));
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000248 exit(1);
249 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400250
JP Abgralle0ed7402014-03-19 19:08:39 -0700251 hdr.offset_blockmap = seek_relative(fd, 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000252 retval = ext2fs_image_bitmap_write(fs, fd, 0);
253 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700254 com_err(program_name, retval, "%s",
255 _("while writing block bitmap"));
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000256 exit(1);
257 }
258
JP Abgralle0ed7402014-03-19 19:08:39 -0700259 hdr.offset_inodemap = seek_relative(fd, 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000260 retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
261 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700262 com_err(program_name, retval, "%s",
263 _("while writing inode bitmap"));
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000264 exit(1);
265 }
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000266
267 hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
268 strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
269 gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
Theodore Ts'odf200ff2008-01-27 15:45:30 -0500270 strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1);
Theodore Ts'o095b4592001-05-03 13:33:11 +0000271 hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
272 hdr.fs_blocksize = fs->blocksize;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400273
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000274 if (stat(device_name, &st) == 0)
275 hdr.fs_device = st.st_rdev;
276
277 if (fstat(fd, &st) == 0) {
278 hdr.image_device = st.st_dev;
279 hdr.image_inode = st.st_ino;
280 }
281 memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
282
283 hdr.image_time = time(0);
JP Abgralle0ed7402014-03-19 19:08:39 -0700284 write_header(fd, &hdr, sizeof(struct ext2_image_hdr), fs->blocksize);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400285}
286
287/*
288 * These set of functions are used to write a RAW image file.
289 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700290static ext2fs_block_bitmap meta_block_map;
291static ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */
292static blk64_t meta_blocks_count;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400293
294struct process_block_struct {
295 ext2_ino_t ino;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500296 int is_dir;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400297};
298
299/*
300 * These subroutines short circuits ext2fs_get_blocks and
301 * ext2fs_check_directory; we use them since we already have the inode
302 * structure, so there's no point in letting the ext2fs library read
303 * the inode again.
304 */
305static ino_t stashed_ino = 0;
306static struct ext2_inode *stashed_inode;
307
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400308static errcode_t meta_get_blocks(ext2_filsys fs EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500309 ext2_ino_t ino,
310 blk_t *blocks)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400311{
312 int i;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400313
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400314 if ((ino != stashed_ino) || !stashed_inode)
315 return EXT2_ET_CALLBACK_NOTHANDLED;
316
317 for (i=0; i < EXT2_N_BLOCKS; i++)
318 blocks[i] = stashed_inode->i_block[i];
319 return 0;
320}
321
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400322static errcode_t meta_check_directory(ext2_filsys fs EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500323 ext2_ino_t ino)
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500324{
325 if ((ino != stashed_ino) || !stashed_inode)
326 return EXT2_ET_CALLBACK_NOTHANDLED;
327
328 if (!LINUX_S_ISDIR(stashed_inode->i_mode))
329 return EXT2_ET_NO_DIRECTORY;
330 return 0;
331}
332
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400333static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500334 ext2_ino_t ino,
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400335 struct ext2_inode *inode)
336{
337 if ((ino != stashed_ino) || !stashed_inode)
338 return EXT2_ET_CALLBACK_NOTHANDLED;
339 *inode = *stashed_inode;
340 return 0;
341}
342
JP Abgralle0ed7402014-03-19 19:08:39 -0700343static void use_inode_shortcuts(ext2_filsys fs, int use_shortcuts)
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500344{
JP Abgralle0ed7402014-03-19 19:08:39 -0700345 if (use_shortcuts) {
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500346 fs->get_blocks = meta_get_blocks;
347 fs->check_directory = meta_check_directory;
348 fs->read_inode = meta_read_inode;
349 stashed_ino = 0;
350 } else {
351 fs->get_blocks = 0;
352 fs->check_directory = 0;
353 fs->read_inode = 0;
354 }
355}
356
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400357static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
JP Abgralle0ed7402014-03-19 19:08:39 -0700358 blk64_t *block_nr,
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400359 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
JP Abgralle0ed7402014-03-19 19:08:39 -0700360 blk64_t ref_block EXT2FS_ATTR((unused)),
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400361 int ref_offset EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500362 void *priv_data EXT2FS_ATTR((unused)))
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400363{
Theodore Ts'od851ed32005-01-19 00:26:43 -0500364 struct process_block_struct *p;
365
366 p = (struct process_block_struct *) priv_data;
367
JP Abgralle0ed7402014-03-19 19:08:39 -0700368 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
369 meta_blocks_count++;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400370 if (scramble_block_map && p->is_dir && blockcnt >= 0)
JP Abgralle0ed7402014-03-19 19:08:39 -0700371 ext2fs_mark_block_bitmap2(scramble_block_map, *block_nr);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400372 return 0;
373}
374
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400375static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)),
JP Abgralle0ed7402014-03-19 19:08:39 -0700376 blk64_t *block_nr,
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400377 e2_blkcnt_t blockcnt,
JP Abgralle0ed7402014-03-19 19:08:39 -0700378 blk64_t ref_block EXT2FS_ATTR((unused)),
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400379 int ref_offset EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500380 void *priv_data EXT2FS_ATTR((unused)))
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400381{
JP Abgralle0ed7402014-03-19 19:08:39 -0700382 if (blockcnt < 0 || all_data) {
383 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
384 meta_blocks_count++;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400385 }
386 return 0;
387}
388
389static void mark_table_blocks(ext2_filsys fs)
390{
JP Abgralle0ed7402014-03-19 19:08:39 -0700391 blk64_t first_block, b;
Theodore Ts'o54434922003-12-07 01:28:50 -0500392 unsigned int i,j;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400393
Eric Sandeenbb1a46a2006-09-12 14:55:22 -0400394 first_block = fs->super->s_first_data_block;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400395 /*
396 * Mark primary superblock
397 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700398 ext2fs_mark_block_bitmap2(meta_block_map, first_block);
399 meta_blocks_count++;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400400
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400401 /*
402 * Mark the primary superblock descriptors
403 */
404 for (j = 0; j < fs->desc_blocks; j++) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700405 ext2fs_mark_block_bitmap2(meta_block_map,
406 ext2fs_descriptor_block_loc2(fs, first_block, j));
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400407 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700408 meta_blocks_count += fs->desc_blocks;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400409
410 for (i = 0; i < fs->group_desc_count; i++) {
411 /*
412 * Mark the blocks used for the inode table
413 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700414 if ((output_is_blk ||
415 !ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT)) &&
416 ext2fs_inode_table_loc(fs, i)) {
417 unsigned int end = (unsigned) fs->inode_blocks_per_group;
418 /* skip unused blocks */
419 if (!output_is_blk &&
420 EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
421 EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
422 end -= (ext2fs_bg_itable_unused(fs, i) /
423 EXT2_INODES_PER_BLOCK(fs->super));
424 for (j = 0, b = ext2fs_inode_table_loc(fs, i);
425 j < end;
426 j++, b++) {
427 ext2fs_mark_block_bitmap2(meta_block_map, b);
428 meta_blocks_count++;
429 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400430 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400431
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400432 /*
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400433 * Mark block used for the block bitmap
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400434 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700435 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) &&
436 ext2fs_block_bitmap_loc(fs, i)) {
437 ext2fs_mark_block_bitmap2(meta_block_map,
438 ext2fs_block_bitmap_loc(fs, i));
439 meta_blocks_count++;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400440 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400441
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400442 /*
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400443 * Mark block used for the inode bitmap
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400444 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700445 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) &&
446 ext2fs_inode_bitmap_loc(fs, i)) {
447 ext2fs_mark_block_bitmap2(meta_block_map,
448 ext2fs_inode_bitmap_loc(fs, i));
449 meta_blocks_count++;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400450 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400451 }
452}
453
454/*
455 * This function returns 1 if the specified block is all zeros
456 */
457static int check_zero_block(char *buf, int blocksize)
458{
459 char *cp = buf;
460 int left = blocksize;
461
JP Abgralle0ed7402014-03-19 19:08:39 -0700462 if (output_is_blk)
463 return 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400464 while (left > 0) {
465 if (*cp++)
466 return 0;
467 left--;
468 }
469 return 1;
470}
471
JP Abgralle0ed7402014-03-19 19:08:39 -0700472static int name_id[256];
Theodore Ts'od851ed32005-01-19 00:26:43 -0500473
Theodore Ts'o8a480352009-06-21 21:07:38 -0400474#define EXT4_MAX_REC_LEN ((1<<16)-1)
475
JP Abgralle0ed7402014-03-19 19:08:39 -0700476static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf)
Theodore Ts'od851ed32005-01-19 00:26:43 -0500477{
478 char *p, *end, *cp;
479 struct ext2_dir_entry_2 *dirent;
Theodore Ts'o8a480352009-06-21 21:07:38 -0400480 unsigned int rec_len;
481 int id, len;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500482
Theodore Ts'od851ed32005-01-19 00:26:43 -0500483 end = buf + fs->blocksize;
484 for (p = buf; p < end-8; p += rec_len) {
485 dirent = (struct ext2_dir_entry_2 *) p;
486 rec_len = dirent->rec_len;
Theodore Ts'o2eae0932007-08-11 02:57:31 -0400487#ifdef WORDS_BIGENDIAN
488 rec_len = ext2fs_swab16(rec_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500489#endif
Theodore Ts'o8a480352009-06-21 21:07:38 -0400490 if (rec_len == EXT4_MAX_REC_LEN || rec_len == 0)
491 rec_len = fs->blocksize;
492 else
493 rec_len = (rec_len & 65532) | ((rec_len & 3) << 16);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500494#if 0
495 printf("rec_len = %d, name_len = %d\n", rec_len, dirent->name_len);
496#endif
497 if (rec_len < 8 || (rec_len % 4) ||
498 (p+rec_len > end)) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700499 printf(_("Corrupt directory block %llu: "
500 "bad rec_len (%d)\n"),
501 (unsigned long long) blk, rec_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500502 rec_len = end - p;
Theodore Ts'o8a480352009-06-21 21:07:38 -0400503 (void) ext2fs_set_rec_len(fs, rec_len,
504 (struct ext2_dir_entry *) dirent);
Theodore Ts'o2eae0932007-08-11 02:57:31 -0400505#ifdef WORDS_BIGENDIAN
Theodore Ts'o8a480352009-06-21 21:07:38 -0400506 dirent->rec_len = ext2fs_swab16(dirent->rec_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500507#endif
508 continue;
509 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700510 if (dirent->name_len + 8U > rec_len) {
511 printf(_("Corrupt directory block %llu: "
512 "bad name_len (%d)\n"),
513 (unsigned long long) blk, dirent->name_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500514 dirent->name_len = rec_len - 8;
515 continue;
516 }
Theodore Ts'od851ed32005-01-19 00:26:43 -0500517 cp = p+8;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500518 len = rec_len - dirent->name_len - 8;
519 if (len > 0)
520 memset(cp+dirent->name_len, 0, len);
Theodore Ts'o79fc2a92005-01-26 11:37:46 -0500521 if (dirent->name_len==1 && cp[0] == '.')
522 continue;
523 if (dirent->name_len==2 && cp[0] == '.' && cp[1] == '.')
524 continue;
525
526 memset(cp, 'A', dirent->name_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500527 len = dirent->name_len;
528 id = name_id[len]++;
529 while ((len > 0) && (id > 0)) {
530 *cp += id % 26;
531 id = id / 26;
532 cp++;
533 len--;
534 }
535 }
536}
537
JP Abgralle0ed7402014-03-19 19:08:39 -0700538static char got_sigint;
539
540static void sigint_handler(int unused EXT2FS_ATTR((unused)))
541{
542 got_sigint = 1;
543 signal (SIGINT, SIG_DFL);
544}
545
546#define calc_percent(a, b) ((int) ((100.0 * (((float) (a)) / \
547 ((float) (b)))) + 0.5))
548#define calc_rate(t, b, d) (((float)(t) / ((1024 * 1024) / (b))) / (d))
549
550static int print_progress(blk64_t num, blk64_t total)
551{
552 return fprintf(stderr, _("%llu / %llu blocks (%d%%)"), num, total,
553 calc_percent(num, total));
554}
555
556static void output_meta_data_blocks(ext2_filsys fs, int fd, int flags)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400557{
558 errcode_t retval;
JP Abgralle0ed7402014-03-19 19:08:39 -0700559 blk64_t blk;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500560 char *buf, *zero_buf;
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500561 int sparse = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -0700562 blk64_t start = 0;
563 blk64_t distance = 0;
564 blk64_t end = ext2fs_blocks_count(fs->super);
565 time_t last_update = 0;
566 time_t start_time = 0;
567 blk64_t total_written = 0;
568 int bscount = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400569
JP Abgralle0ed7402014-03-19 19:08:39 -0700570 retval = ext2fs_get_mem(fs->blocksize, &buf);
571 if (retval) {
572 com_err(program_name, retval, _("while allocating buffer"));
Theodore Ts'od851ed32005-01-19 00:26:43 -0500573 exit(1);
574 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700575 retval = ext2fs_get_memzero(fs->blocksize, &zero_buf);
576 if (retval) {
577 com_err(program_name, retval, _("while allocating buffer"));
Theodore Ts'od851ed32005-01-19 00:26:43 -0500578 exit(1);
579 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700580 if (show_progress) {
581 fprintf(stderr, _("Copying "));
582 bscount = print_progress(total_written, meta_blocks_count);
583 fflush(stderr);
584 last_update = time(NULL);
585 start_time = time(NULL);
586 }
587 /* when doing an in place move to the right, you can't start
588 at the beginning or you will overwrite data, so instead
589 divide the fs up into distance size chunks and write them
590 in reverse. */
591 if (move_mode && dest_offset > source_offset) {
592 distance = (dest_offset - source_offset) / fs->blocksize;
593 if (distance < ext2fs_blocks_count(fs->super))
594 start = ext2fs_blocks_count(fs->super) - distance;
595 }
596 if (move_mode)
597 signal (SIGINT, sigint_handler);
598more_blocks:
599 if (distance)
600 seek_set(fd, (start * fs->blocksize) + dest_offset);
601 for (blk = start; blk < end; blk++) {
602 if (got_sigint) {
603 if (distance) {
604 /* moving to the right */
605 if (distance >= ext2fs_blocks_count(fs->super) ||
606 start == ext2fs_blocks_count(fs->super) - distance)
607 kill (getpid(), SIGINT);
608 } else {
609 /* moving to the left */
610 if (blk < (source_offset - dest_offset) / fs->blocksize)
611 kill (getpid(), SIGINT);
612 }
613 if (show_progress)
614 fputc('\r', stderr);
615 fprintf(stderr,
616 _("Stopping now will destroy the filesystem, "
617 "interrupt again if you are sure\n"));
618 if (show_progress) {
619 fprintf(stderr, _("Copying "));
620 bscount = print_progress(total_written,
621 meta_blocks_count);
622 fflush(stderr);
623 }
624
625 got_sigint = 0;
626 }
627 if (show_progress && last_update != time(NULL)) {
628 time_t duration;
629 last_update = time(NULL);
630 while (bscount--)
631 fputc('\b', stderr);
632 bscount = print_progress(total_written,
633 meta_blocks_count);
634 duration = time(NULL) - start_time;
635 if (duration > 5) {
636 time_t est = (duration * meta_blocks_count /
637 total_written) - duration;
638 char buff[30];
639 strftime(buff, 30, "%T", gmtime(&est));
640 bscount += fprintf(stderr,
641 _(" %s remaining at %.2f MB/s"),
642 buff, calc_rate(total_written,
643 fs->blocksize,
644 duration));
645 }
646 fflush (stderr);
647 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400648 if ((blk >= fs->super->s_first_data_block) &&
JP Abgralle0ed7402014-03-19 19:08:39 -0700649 ext2fs_test_block_bitmap2(meta_block_map, blk)) {
650 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400651 if (retval) {
652 com_err(program_name, retval,
JP Abgralle0ed7402014-03-19 19:08:39 -0700653 _("error reading block %llu"), blk);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400654 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700655 total_written++;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400656 if (scramble_block_map &&
JP Abgralle0ed7402014-03-19 19:08:39 -0700657 ext2fs_test_block_bitmap2(scramble_block_map, blk))
Theodore Ts'od851ed32005-01-19 00:26:43 -0500658 scramble_dir_block(fs, blk, buf);
JP Abgralle0ed7402014-03-19 19:08:39 -0700659 if ((flags & E2IMAGE_CHECK_ZERO_FLAG) &&
660 check_zero_block(buf, fs->blocksize))
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400661 goto sparse_write;
JP Abgralle0ed7402014-03-19 19:08:39 -0700662 if (sparse)
663 seek_relative(fd, sparse);
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500664 sparse = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -0700665 if (check_block(fd, buf, check_buf, fs->blocksize)) {
666 seek_relative(fd, fs->blocksize);
667 skipped_blocks++;
668 } else
669 generic_write(fd, buf, fs->blocksize, blk);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400670 } else {
671 sparse_write:
672 if (fd == 1) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700673 if (!nop_flag)
674 generic_write(fd, zero_buf,
675 fs->blocksize, blk);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400676 continue;
677 }
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500678 sparse += fs->blocksize;
JP Abgralle0ed7402014-03-19 19:08:39 -0700679 if (sparse > 1024*1024) {
680 seek_relative(fd, 1024*1024);
681 sparse -= 1024*1024;
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500682 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400683 }
684 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700685 if (distance && start) {
686 if (start < distance) {
687 end = start;
688 start = 0;
689 } else {
690 end -= distance;
691 start -= distance;
692 if (end < distance) {
693 /* past overlap, do rest in one go */
694 end = start;
695 start = 0;
696 }
697 }
698 sparse = 0;
699 goto more_blocks;
700 }
701 signal (SIGINT, SIG_DFL);
702 if (show_progress) {
703 time_t duration = time(NULL) - start_time;
704 char buff[30];
705 while (bscount--)
706 fputc('\b', stderr);
707 strftime(buff, 30, "%T", gmtime(&duration));
708 fprintf(stderr, _("\b\b\b\b\b\b\b\bCopied %llu / %llu "
709 "blocks (%llu%%) in %s at %.2f MB/s \n"),
710 total_written, meta_blocks_count,
711 calc_percent(total_written, meta_blocks_count), buff,
712 calc_rate(total_written, fs->blocksize, duration));
713 }
714#ifdef HAVE_FTRUNCATE64
715 if (sparse) {
716 ext2_loff_t offset;
717 if (distance)
718 offset = seek_set(fd,
719 fs->blocksize * ext2fs_blocks_count(fs->super) + dest_offset);
720 else
721 offset = seek_relative(fd, sparse);
722
723 if (ftruncate64(fd, offset) < 0) {
724 seek_relative(fd, -1);
725 generic_write(fd, zero_buf, 1, NO_BLK);
726 }
727 }
728#else
729 if (sparse && !distance) {
730 seek_relative(fd, sparse-1);
731 generic_write(fd, zero_buf, 1, NO_BLK);
732 }
733#endif
734 ext2fs_free_mem(&zero_buf);
735 ext2fs_free_mem(&buf);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400736}
737
JP Abgralle0ed7402014-03-19 19:08:39 -0700738static void init_l1_table(struct ext2_qcow2_image *image)
739{
740 __u64 *l1_table;
741 errcode_t ret;
742
743 ret = ext2fs_get_arrayzero(image->l1_size, sizeof(__u64), &l1_table);
744 if (ret) {
745 com_err(program_name, ret, _("while allocating l1 table"));
746 exit(1);
747 }
748
749 image->l1_table = l1_table;
750}
751
752static void init_l2_cache(struct ext2_qcow2_image *image)
753{
754 unsigned int count, i;
755 struct ext2_qcow2_l2_cache *cache;
756 struct ext2_qcow2_l2_table *table;
757 errcode_t ret;
758
759 ret = ext2fs_get_arrayzero(1, sizeof(struct ext2_qcow2_l2_cache),
760 &cache);
761 if (ret)
762 goto alloc_err;
763
764 count = (image->l1_size > L2_CACHE_PREALLOC) ? L2_CACHE_PREALLOC :
765 image->l1_size;
766
767 cache->count = count;
768 cache->free = count;
769 cache->next_offset = image->l2_offset;
770
771 for (i = 0; i < count; i++) {
772 ret = ext2fs_get_arrayzero(1,
773 sizeof(struct ext2_qcow2_l2_table), &table);
774 if (ret)
775 goto alloc_err;
776
777 ret = ext2fs_get_arrayzero(image->l2_size,
778 sizeof(__u64), &table->data);
779 if (ret)
780 goto alloc_err;
781
782 table->next = cache->free_head;
783 cache->free_head = table;
784 }
785
786 image->l2_cache = cache;
787 return;
788
789alloc_err:
790 com_err(program_name, ret, _("while allocating l2 cache"));
791 exit(1);
792}
793
794static void put_l2_cache(struct ext2_qcow2_image *image)
795{
796 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
797 struct ext2_qcow2_l2_table *tmp, *table;
798
799 if (!cache)
800 return;
801
802 table = cache->free_head;
803 cache->free_head = NULL;
804again:
805 while (table) {
806 tmp = table;
807 table = table->next;
808 ext2fs_free_mem(&tmp->data);
809 ext2fs_free_mem(&tmp);
810 }
811
812 if (cache->free != cache->count) {
813 fprintf(stderr, _("Warning: There are still tables in the "
814 "cache while putting the cache, data will "
815 "be lost so the image may not be valid.\n"));
816 table = cache->used_head;
817 cache->used_head = NULL;
818 goto again;
819 }
820
821 ext2fs_free_mem(&cache);
822}
823
824static int init_refcount(struct ext2_qcow2_image *img, blk64_t table_offset)
825{
826 struct ext2_qcow2_refcount *ref;
827 blk64_t table_clusters;
828 errcode_t ret;
829
830 ref = &(img->refcount);
831
832 /*
833 * One refcount block addresses 2048 clusters, one refcount table
834 * addresses cluster/sizeof(__u64) refcount blocks, and we need
835 * to address meta_blocks_count clusters + qcow2 metadata clusters
836 * in the worst case.
837 */
838 table_clusters = meta_blocks_count + (table_offset >>
839 img->cluster_bits);
840 table_clusters >>= (img->cluster_bits + 6 - 1);
841 table_clusters = (table_clusters == 0) ? 1 : table_clusters;
842
843 ref->refcount_table_offset = table_offset;
844 ref->refcount_table_clusters = table_clusters;
845 ref->refcount_table_index = 0;
846 ref->refcount_block_index = 0;
847
848 /* Allocate refcount table */
849 ret = ext2fs_get_arrayzero(ref->refcount_table_clusters,
850 img->cluster_size, &ref->refcount_table);
851 if (ret)
852 return ret;
853
854 /* Allocate refcount block */
855 ret = ext2fs_get_arrayzero(1, img->cluster_size, &ref->refcount_block);
856 if (ret)
857 ext2fs_free_mem(&ref->refcount_table);
858
859 return ret;
860}
861
862static int initialize_qcow2_image(int fd, ext2_filsys fs,
863 struct ext2_qcow2_image *image)
864{
865 struct ext2_qcow2_hdr *header;
866 blk64_t total_size, offset;
867 int shift, l2_bits, header_size, l1_size, ret;
868 int cluster_bits = get_bits_from_size(fs->blocksize);
869 struct ext2_super_block *sb = fs->super;
870
871 /* Allocate header */
872 ret = ext2fs_get_memzero(sizeof(struct ext2_qcow2_hdr), &header);
873 if (ret)
874 return ret;
875
876 total_size = ext2fs_blocks_count(sb) << cluster_bits;
877 image->cluster_size = fs->blocksize;
878 image->l2_size = 1 << (cluster_bits - 3);
879 image->cluster_bits = cluster_bits;
880 image->fd = fd;
881
882 header->magic = ext2fs_cpu_to_be32(QCOW_MAGIC);
883 header->version = ext2fs_cpu_to_be32(QCOW_VERSION);
884 header->size = ext2fs_cpu_to_be64(total_size);
885 header->cluster_bits = ext2fs_cpu_to_be32(cluster_bits);
886
887 header_size = (sizeof(struct ext2_qcow2_hdr) + 7) & ~7;
888 offset = align_offset(header_size, image->cluster_size);
889
890 header->l1_table_offset = ext2fs_cpu_to_be64(offset);
891 image->l1_offset = offset;
892
893 l2_bits = cluster_bits - 3;
894 shift = cluster_bits + l2_bits;
895 l1_size = ((total_size + (1LL << shift) - 1) >> shift);
896 header->l1_size = ext2fs_cpu_to_be32(l1_size);
897 image->l1_size = l1_size;
898
899 /* Make space for L1 table */
900 offset += align_offset(l1_size * sizeof(blk64_t), image->cluster_size);
901
902 /* Initialize refcounting */
903 ret = init_refcount(image, offset);
904 if (ret) {
905 ext2fs_free_mem(&header);
906 return ret;
907 }
908 header->refcount_table_offset = ext2fs_cpu_to_be64(offset);
909 header->refcount_table_clusters =
910 ext2fs_cpu_to_be32(image->refcount.refcount_table_clusters);
911 offset += image->cluster_size;
912 offset += image->refcount.refcount_table_clusters <<
913 image->cluster_bits;
914
915 /* Make space for L2 tables */
916 image->l2_offset = offset;
917 offset += image->cluster_size;
918
919 /* Make space for first refcount block */
920 image->refcount.refcount_block_offset = offset;
921
922 image->hdr = header;
923 /* Initialize l1 and l2 tables */
924 init_l1_table(image);
925 init_l2_cache(image);
926
927 return 0;
928}
929
930static void free_qcow2_image(struct ext2_qcow2_image *img)
931{
932 if (!img)
933 return;
934
935 if (img->hdr)
936 ext2fs_free_mem(&img->hdr);
937
938 if (img->l1_table)
939 ext2fs_free_mem(&img->l1_table);
940
941 if (img->refcount.refcount_table)
942 ext2fs_free_mem(&img->refcount.refcount_table);
943 if (img->refcount.refcount_block)
944 ext2fs_free_mem(&img->refcount.refcount_block);
945
946 put_l2_cache(img);
947
948 ext2fs_free_mem(&img);
949}
950
951/**
952 * Put table from used list (used_head) into free list (free_head).
953 * l2_table is used to return pointer to the next used table (used_head).
954 */
955static void put_used_table(struct ext2_qcow2_image *img,
956 struct ext2_qcow2_l2_table **l2_table)
957{
958 struct ext2_qcow2_l2_cache *cache = img->l2_cache;
959 struct ext2_qcow2_l2_table *table;
960
961 table = cache->used_head;
962 cache->used_head = table->next;
963
964 assert(table);
965 if (!table->next)
966 cache->used_tail = NULL;
967
968 /* Clean the table for case we will need to use it again */
969 memset(table->data, 0, img->cluster_size);
970 table->next = cache->free_head;
971 cache->free_head = table;
972
973 cache->free++;
974
975 *l2_table = cache->used_head;
976}
977
978static void flush_l2_cache(struct ext2_qcow2_image *image)
979{
980 blk64_t seek = 0;
981 ext2_loff_t offset;
982 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
983 struct ext2_qcow2_l2_table *table = cache->used_head;
984 int fd = image->fd;
985
986 /* Store current position */
987 offset = seek_relative(fd, 0);
988
989 assert(table);
990 while (cache->free < cache->count) {
991 if (seek != table->offset) {
992 seek_set(fd, table->offset);
993 seek = table->offset;
994 }
995
996 generic_write(fd, (char *)table->data, image->cluster_size,
997 NO_BLK);
998 put_used_table(image, &table);
999 seek += image->cluster_size;
1000 }
1001
1002 /* Restore previous position */
1003 seek_set(fd, offset);
1004}
1005
1006/**
1007 * Get first free table (from free_head) and put it into tail of used list
1008 * (to used_tail).
1009 * l2_table is used to return pointer to moved table.
1010 * Returns 1 if the cache is full, 0 otherwise.
1011 */
1012static void get_free_table(struct ext2_qcow2_image *image,
1013 struct ext2_qcow2_l2_table **l2_table)
1014{
1015 struct ext2_qcow2_l2_table *table;
1016 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
1017
1018 if (0 == cache->free)
1019 flush_l2_cache(image);
1020
1021 table = cache->free_head;
1022 assert(table);
1023 cache->free_head = table->next;
1024
1025 if (cache->used_tail)
1026 cache->used_tail->next = table;
1027 else
1028 /* First item in the used list */
1029 cache->used_head = table;
1030
1031 cache->used_tail = table;
1032 cache->free--;
1033
1034 *l2_table = table;
1035}
1036
1037static int add_l2_item(struct ext2_qcow2_image *img, blk64_t blk,
1038 blk64_t data, blk64_t next)
1039{
1040 struct ext2_qcow2_l2_cache *cache = img->l2_cache;
1041 struct ext2_qcow2_l2_table *table = cache->used_tail;
1042 blk64_t l1_index = blk / img->l2_size;
1043 blk64_t l2_index = blk & (img->l2_size - 1);
1044 int ret = 0;
1045
1046 /*
1047 * Need to create new table if it does not exist,
1048 * or if it is full
1049 */
1050 if (!table || (table->l1_index != l1_index)) {
1051 get_free_table(img, &table);
1052 table->l1_index = l1_index;
1053 table->offset = cache->next_offset;
1054 cache->next_offset = next;
1055 img->l1_table[l1_index] =
1056 ext2fs_cpu_to_be64(table->offset | QCOW_OFLAG_COPIED);
1057 ret++;
1058 }
1059
1060 table->data[l2_index] = ext2fs_cpu_to_be64(data | QCOW_OFLAG_COPIED);
1061 return ret;
1062}
1063
1064static int update_refcount(int fd, struct ext2_qcow2_image *img,
1065 blk64_t offset, blk64_t rfblk_pos)
1066{
1067 struct ext2_qcow2_refcount *ref;
1068 __u32 table_index;
1069 int ret = 0;
1070
1071 ref = &(img->refcount);
1072 table_index = offset >> (2 * img->cluster_bits - 1);
1073
1074 /*
1075 * Need to create new refcount block when the offset addresses
1076 * another item in the refcount table
1077 */
1078 if (table_index != ref->refcount_table_index) {
1079
1080 seek_set(fd, ref->refcount_block_offset);
1081
1082 generic_write(fd, (char *)ref->refcount_block,
1083 img->cluster_size, NO_BLK);
1084 memset(ref->refcount_block, 0, img->cluster_size);
1085
1086 ref->refcount_table[ref->refcount_table_index] =
1087 ext2fs_cpu_to_be64(ref->refcount_block_offset);
1088 ref->refcount_block_offset = rfblk_pos;
1089 ref->refcount_block_index = 0;
1090 ref->refcount_table_index = table_index;
1091 ret++;
1092 }
1093
1094 /*
1095 * We are relying on the fact that we are creating the qcow2
1096 * image sequentially, hence we will always allocate refcount
1097 * block items sequentialy.
1098 */
1099 ref->refcount_block[ref->refcount_block_index] = ext2fs_cpu_to_be16(1);
1100 ref->refcount_block_index++;
1101 return ret;
1102}
1103
1104static int sync_refcount(int fd, struct ext2_qcow2_image *img)
1105{
1106 struct ext2_qcow2_refcount *ref;
1107
1108 ref = &(img->refcount);
1109
1110 ref->refcount_table[ref->refcount_table_index] =
1111 ext2fs_cpu_to_be64(ref->refcount_block_offset);
1112 seek_set(fd, ref->refcount_table_offset);
1113 generic_write(fd, (char *)ref->refcount_table,
1114 ref->refcount_table_clusters << img->cluster_bits, NO_BLK);
1115
1116 seek_set(fd, ref->refcount_block_offset);
1117 generic_write(fd, (char *)ref->refcount_block, img->cluster_size,
1118 NO_BLK);
1119 return 0;
1120}
1121
1122static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd)
1123{
1124 errcode_t retval;
1125 blk64_t blk, offset, size, end;
1126 char *buf;
1127 struct ext2_qcow2_image *img;
1128 unsigned int header_size;
1129
1130 /* allocate struct ext2_qcow2_image */
1131 retval = ext2fs_get_mem(sizeof(struct ext2_qcow2_image), &img);
1132 if (retval) {
1133 com_err(program_name, retval,
1134 _("while allocating ext2_qcow2_image"));
1135 exit(1);
1136 }
1137
1138 retval = initialize_qcow2_image(fd, fs, img);
1139 if (retval) {
1140 com_err(program_name, retval,
1141 _("while initializing ext2_qcow2_image"));
1142 exit(1);
1143 }
1144 header_size = align_offset(sizeof(struct ext2_qcow2_hdr),
1145 img->cluster_size);
1146 write_header(fd, img->hdr, sizeof(struct ext2_qcow2_hdr), header_size);
1147
1148 /* Refcount all qcow2 related metadata up to refcount_block_offset */
1149 end = img->refcount.refcount_block_offset;
1150 seek_set(fd, end);
1151 blk = end + img->cluster_size;
1152 for (offset = 0; offset <= end; offset += img->cluster_size) {
1153 if (update_refcount(fd, img, offset, blk)) {
1154 blk += img->cluster_size;
1155 /*
1156 * If we create new refcount block, we need to refcount
1157 * it as well.
1158 */
1159 end += img->cluster_size;
1160 }
1161 }
1162 seek_set(fd, offset);
1163
1164 retval = ext2fs_get_mem(fs->blocksize, &buf);
1165 if (retval) {
1166 com_err(program_name, retval, _("while allocating buffer"));
1167 exit(1);
1168 }
1169 /* Write qcow2 data blocks */
1170 for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) {
1171 if ((blk >= fs->super->s_first_data_block) &&
1172 ext2fs_test_block_bitmap2(meta_block_map, blk)) {
1173 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
1174 if (retval) {
1175 com_err(program_name, retval,
1176 _("error reading block %llu"), blk);
1177 continue;
1178 }
1179 if (scramble_block_map &&
1180 ext2fs_test_block_bitmap2(scramble_block_map, blk))
1181 scramble_dir_block(fs, blk, buf);
1182 if (check_zero_block(buf, fs->blocksize))
1183 continue;
1184
1185 if (update_refcount(fd, img, offset, offset)) {
1186 /* Make space for another refcount block */
1187 offset += img->cluster_size;
1188 seek_set(fd, offset);
1189 /*
1190 * We have created the new refcount block, this
1191 * means that we need to refcount it as well.
1192 * So the previous update_refcount refcounted
1193 * the block itself and now we are going to
1194 * create refcount for data. New refcount
1195 * block should not be created!
1196 */
1197 if (update_refcount(fd, img, offset, offset)) {
1198 fprintf(stderr, _("Programming error: "
1199 "multiple sequential refcount "
1200 "blocks created!\n"));
1201 exit(1);
1202 }
1203 }
1204
1205 generic_write(fd, buf, fs->blocksize, blk);
1206
1207 if (add_l2_item(img, blk, offset,
1208 offset + img->cluster_size)) {
1209 offset += img->cluster_size;
1210 if (update_refcount(fd, img, offset,
1211 offset + img->cluster_size)) {
1212 offset += img->cluster_size;
1213 if (update_refcount(fd, img, offset,
1214 offset)) {
1215 fprintf(stderr,
1216 _("Programming error: multiple sequential refcount "
1217 "blocks created!\n"));
1218 exit(1);
1219 }
1220 }
1221 offset += img->cluster_size;
1222 seek_set(fd, offset);
1223 continue;
1224 }
1225
1226 offset += img->cluster_size;
1227 }
1228 }
1229 update_refcount(fd, img, offset, offset);
1230 flush_l2_cache(img);
1231 sync_refcount(fd, img);
1232
1233 /* Write l1_table*/
1234 seek_set(fd, img->l1_offset);
1235 size = img->l1_size * sizeof(__u64);
1236 generic_write(fd, (char *)img->l1_table, size, NO_BLK);
1237
1238 ext2fs_free_mem(&buf);
1239 free_qcow2_image(img);
1240}
1241
1242static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001243{
1244 struct process_block_struct pb;
1245 struct ext2_inode inode;
1246 ext2_inode_scan scan;
1247 ext2_ino_t ino;
1248 errcode_t retval;
1249 char * block_buf;
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001250
JP Abgralle0ed7402014-03-19 19:08:39 -07001251 meta_blocks_count = 0;
1252 retval = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001253 &meta_block_map);
1254 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001255 com_err(program_name, retval,
1256 _("while allocating block bitmap"));
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001257 exit(1);
1258 }
Theodore Ts'od851ed32005-01-19 00:26:43 -05001259
JP Abgralle0ed7402014-03-19 19:08:39 -07001260 if (flags & E2IMAGE_SCRAMBLE_FLAG) {
Theodore Ts'od851ed32005-01-19 00:26:43 -05001261 retval = ext2fs_allocate_block_bitmap(fs, "scramble block map",
1262 &scramble_block_map);
1263 if (retval) {
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001264 com_err(program_name, retval,
JP Abgralle0ed7402014-03-19 19:08:39 -07001265 _("while allocating scramble block bitmap"));
Theodore Ts'od851ed32005-01-19 00:26:43 -05001266 exit(1);
1267 }
1268 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001269
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001270 mark_table_blocks(fs);
JP Abgralle0ed7402014-03-19 19:08:39 -07001271 if (show_progress)
1272 printf(_("Scanning inodes...\n"));
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001273
1274 retval = ext2fs_open_inode_scan(fs, 0, &scan);
1275 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001276 com_err(program_name, retval,"%s",
1277 _("while opening inode scan"));
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001278 exit(1);
1279 }
1280
JP Abgralle0ed7402014-03-19 19:08:39 -07001281 retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
1282 if (retval) {
1283 com_err(program_name, 0, "%s",
1284 _("Can't allocate block buffer"));
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001285 exit(1);
1286 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001287
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -05001288 use_inode_shortcuts(fs, 1);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001289 stashed_inode = &inode;
1290 while (1) {
1291 retval = ext2fs_get_next_inode(scan, &ino, &inode);
Theodore Ts'o3432a912002-10-02 17:47:08 -04001292 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
1293 continue;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001294 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001295 com_err(program_name, retval, "%s",
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001296 _("while getting next inode"));
1297 exit(1);
1298 }
1299 if (ino == 0)
1300 break;
Theodore Ts'oed909bb2002-08-16 17:03:59 -04001301 if (!inode.i_links_count)
1302 continue;
JP Abgralle0ed7402014-03-19 19:08:39 -07001303 if (ext2fs_file_acl_block(fs, &inode)) {
1304 ext2fs_mark_block_bitmap2(meta_block_map,
1305 ext2fs_file_acl_block(fs, &inode));
1306 meta_blocks_count++;
Theodore Ts'oed909bb2002-08-16 17:03:59 -04001307 }
JP Abgralle0ed7402014-03-19 19:08:39 -07001308 if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001309 continue;
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001310
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001311 stashed_ino = ino;
Theodore Ts'od851ed32005-01-19 00:26:43 -05001312 pb.ino = ino;
1313 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001314 if (LINUX_S_ISDIR(inode.i_mode) ||
Theodore Ts'oeca53e32003-03-14 00:38:45 -05001315 (LINUX_S_ISLNK(inode.i_mode) &&
JP Abgralle0ed7402014-03-19 19:08:39 -07001316 ext2fs_inode_has_valid_blocks2(fs, &inode)) ||
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001317 ino == fs->super->s_journal_inum) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001318 retval = ext2fs_block_iterate3(fs, ino,
Theodore Ts'o43323be2008-02-07 14:37:17 -05001319 BLOCK_FLAG_READ_ONLY, block_buf,
1320 process_dir_block, &pb);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001321 if (retval) {
1322 com_err(program_name, retval,
JP Abgralle0ed7402014-03-19 19:08:39 -07001323 _("while iterating over inode %u"),
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001324 ino);
1325 exit(1);
1326 }
1327 } else {
Theodore Ts'o43323be2008-02-07 14:37:17 -05001328 if ((inode.i_flags & EXT4_EXTENTS_FL) ||
1329 inode.i_block[EXT2_IND_BLOCK] ||
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001330 inode.i_block[EXT2_DIND_BLOCK] ||
JP Abgralle0ed7402014-03-19 19:08:39 -07001331 inode.i_block[EXT2_TIND_BLOCK] || all_data) {
1332 retval = ext2fs_block_iterate3(fs,
Theodore Ts'o43323be2008-02-07 14:37:17 -05001333 ino, BLOCK_FLAG_READ_ONLY, block_buf,
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001334 process_file_block, &pb);
1335 if (retval) {
1336 com_err(program_name, retval,
JP Abgralle0ed7402014-03-19 19:08:39 -07001337 _("while iterating over inode %u"), ino);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001338 exit(1);
1339 }
1340 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001341 }
1342 }
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -05001343 use_inode_shortcuts(fs, 0);
JP Abgralle0ed7402014-03-19 19:08:39 -07001344
1345 if (type & E2IMAGE_QCOW2)
1346 output_qcow2_meta_data_blocks(fs, fd);
1347 else
1348 output_meta_data_blocks(fs, fd, flags);
1349
1350 ext2fs_free_mem(&block_buf);
1351 ext2fs_close_inode_scan(scan);
1352 ext2fs_free_block_bitmap(meta_block_map);
1353 if (type & E2IMAGE_SCRAMBLE_FLAG)
1354 ext2fs_free_block_bitmap(scramble_block_map);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001355}
1356
JP Abgralle0ed7402014-03-19 19:08:39 -07001357static void install_image(char *device, char *image_fn, int type)
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001358{
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001359 errcode_t retval;
1360 ext2_filsys fs;
JP Abgralle0ed7402014-03-19 19:08:39 -07001361 int open_flag = EXT2_FLAG_IMAGE_FILE | EXT2_FLAG_64BITS;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001362 int fd = 0;
1363 io_manager io_ptr;
JP Abgralle0ed7402014-03-19 19:08:39 -07001364 io_channel io;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001365
JP Abgralle0ed7402014-03-19 19:08:39 -07001366 if (type) {
1367 com_err(program_name, 0, _("Raw and qcow2 images cannot"
1368 "be installed"));
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001369 exit(1);
1370 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001371
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001372#ifdef CONFIG_TESTIO_DEBUG
Theodore Ts'of38cf3c2008-09-01 11:17:29 -04001373 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
1374 io_ptr = test_io_manager;
1375 test_io_backing_manager = unix_io_manager;
1376 } else
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001377#endif
Theodore Ts'of38cf3c2008-09-01 11:17:29 -04001378 io_ptr = unix_io_manager;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001379
1380 retval = ext2fs_open (image_fn, open_flag, 0, 0,
1381 io_ptr, &fs);
1382 if (retval) {
1383 com_err (program_name, retval, _("while trying to open %s"),
1384 image_fn);
1385 exit(1);
1386 }
1387
1388 retval = ext2fs_read_bitmaps (fs);
1389 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001390 com_err(program_name, retval, _("error reading bitmaps"));
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001391 exit(1);
1392 }
1393
JP Abgralle0ed7402014-03-19 19:08:39 -07001394 fd = ext2fs_open_file(image_fn, O_RDONLY, 0);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001395 if (fd < 0) {
1396 perror(image_fn);
1397 exit(1);
1398 }
1399
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001400 retval = io_ptr->open(device, IO_FLAG_RW, &io);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001401 if (retval) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001402 com_err(device, 0, _("while opening device file"));
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001403 exit(1);
1404 }
1405
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001406 ext2fs_rewrite_to_io(fs, io);
1407
JP Abgralle0ed7402014-03-19 19:08:39 -07001408 seek_set(fd, fs->image_header->offset_inode);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001409
1410 retval = ext2fs_image_inode_read(fs, fd, 0);
1411 if (retval) {
1412 com_err(image_fn, 0, "while restoring the image table");
1413 exit(1);
1414 }
1415
JP Abgralle0ed7402014-03-19 19:08:39 -07001416 close(fd);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001417 ext2fs_close (fs);
JP Abgralle0ed7402014-03-19 19:08:39 -07001418}
1419
1420static struct ext2_qcow2_hdr *check_qcow2_image(int *fd, char *name)
1421{
1422
1423 *fd = ext2fs_open_file(name, O_RDONLY, 0600);
1424 if (*fd < 0)
1425 return NULL;
1426
1427 return qcow2_read_header(*fd);
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001428}
1429
Shay Nachmani03509c92015-01-26 16:59:32 +02001430extern blk_list meta_data_block_list;
1431
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001432int main (int argc, char ** argv)
1433{
1434 int c;
1435 errcode_t retval;
1436 ext2_filsys fs;
JP Abgralle0ed7402014-03-19 19:08:39 -07001437 char *image_fn, offset_opt[64];
1438 struct ext2_qcow2_hdr *header = NULL;
1439 int open_flag = EXT2_FLAG_64BITS;
1440 int img_type = 0;
1441 int flags = 0;
1442 int mount_flags = 0;
1443 int qcow2_fd = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001444 int fd = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -07001445 int ret = 0;
1446 int ignore_rw_mount = 0;
1447 int check = 0;
1448 struct stat st;
Shay Nachmani03509c92015-01-26 16:59:32 +02001449 FILE *block_list_file;
1450 size_t written_objects;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001451
1452#ifdef ENABLE_NLS
1453 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -05001454 setlocale(LC_CTYPE, "");
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001455 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1456 textdomain(NLS_CAT_NAME);
JP Abgralle0ed7402014-03-19 19:08:39 -07001457 set_com_err_gettext(gettext);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001458#endif
Theodore Ts'o0f8973f2001-08-27 12:44:23 -04001459 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
1460 E2FSPROGS_DATE);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001461 if (argc && *argv)
1462 program_name = *argv;
Theodore Ts'oa6d83022006-12-26 03:38:07 -05001463 add_error_table(&et_ext2_error_table);
JP Abgralle0ed7402014-03-19 19:08:39 -07001464 while ((c = getopt(argc, argv, "nrsIQafo:O:pc")) != EOF)
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001465 switch (c) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001466 case 'I':
1467 flags |= E2IMAGE_INSTALL_FLAG;
1468 break;
1469 case 'Q':
1470 if (img_type)
1471 usage();
1472 img_type |= E2IMAGE_QCOW2;
1473 break;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001474 case 'r':
JP Abgralle0ed7402014-03-19 19:08:39 -07001475 if (img_type)
1476 usage();
1477 img_type |= E2IMAGE_RAW;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001478 break;
Theodore Ts'od851ed32005-01-19 00:26:43 -05001479 case 's':
JP Abgralle0ed7402014-03-19 19:08:39 -07001480 flags |= E2IMAGE_SCRAMBLE_FLAG;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001481 break;
JP Abgralle0ed7402014-03-19 19:08:39 -07001482 case 'a':
1483 all_data = 1;
1484 break;
1485 case 'f':
1486 ignore_rw_mount = 1;
1487 break;
1488 case 'n':
1489 nop_flag = 1;
1490 break;
1491 case 'o':
1492 source_offset = strtoull(optarg, NULL, 0);
1493 break;
1494 case 'O':
1495 dest_offset = strtoull(optarg, NULL, 0);
1496 break;
1497 case 'p':
1498 show_progress = 1;
1499 break;
1500 case 'c':
1501 check = 1;
Theodore Ts'o38c771d2013-12-25 16:46:39 -05001502 break;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001503 default:
1504 usage();
1505 }
JP Abgralle0ed7402014-03-19 19:08:39 -07001506 if (optind == argc - 1 &&
1507 (source_offset || dest_offset))
1508 move_mode = 1;
Shay Nachmani03509c92015-01-26 16:59:32 +02001509 else if (optind != argc - 3 )
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001510 usage();
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001511
JP Abgralle0ed7402014-03-19 19:08:39 -07001512 if (all_data && !img_type) {
1513 com_err(program_name, 0, _("-a option can only be used "
1514 "with raw or QCOW2 images."));
1515 exit(1);
1516 }
1517 if ((source_offset || dest_offset) && img_type != E2IMAGE_RAW) {
1518 com_err(program_name, 0,
1519 _("Offsets are only allowed with raw images."));
1520 exit(1);
1521 }
1522 if (move_mode && img_type != E2IMAGE_RAW) {
1523 com_err(program_name, 0,
1524 _("Move mode is only allowed with raw images."));
1525 exit(1);
1526 }
1527 if (move_mode && !all_data) {
1528 com_err(program_name, 0,
1529 _("Move mode requires all data mode."));
1530 exit(1);
1531 }
1532 device_name = argv[optind];
1533 if (move_mode)
1534 image_fn = device_name;
1535 else image_fn = argv[optind+1];
1536
1537 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
1538 if (retval) {
1539 com_err(program_name, retval, _("checking if mounted"));
1540 exit(1);
1541 }
1542
1543 if (img_type && !ignore_rw_mount &&
1544 (mount_flags & EXT2_MF_MOUNTED) &&
1545 !(mount_flags & EXT2_MF_READONLY)) {
1546 fprintf(stderr, _("\nRunning e2image on a R/W mounted "
1547 "filesystem can result in an\n"
1548 "inconsistent image which will not be useful "
1549 "for debugging purposes.\n"
1550 "Use -f option if you really want to do that.\n"));
1551 exit(1);
1552 }
1553
1554 if (flags & E2IMAGE_INSTALL_FLAG) {
1555 install_image(device_name, image_fn, img_type);
Theodore Ts'o6e82cd72005-01-05 03:02:54 -05001556 exit (0);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001557 }
1558
JP Abgralle0ed7402014-03-19 19:08:39 -07001559 if (img_type & E2IMAGE_RAW) {
1560 header = check_qcow2_image(&qcow2_fd, device_name);
1561 if (header) {
1562 flags |= E2IMAGE_IS_QCOW2_FLAG;
1563 goto skip_device;
1564 }
1565 }
1566 sprintf(offset_opt, "offset=%llu", source_offset);
1567 retval = ext2fs_open2(device_name, offset_opt, open_flag, 0, 0,
Theodore Ts'od28759b2013-12-25 14:26:44 -05001568 unix_io_manager, &fs);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001569 if (retval) {
1570 com_err (program_name, retval, _("while trying to open %s"),
1571 device_name);
Theodore Ts'o54434922003-12-07 01:28:50 -05001572 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001573 exit(1);
1574 }
1575
JP Abgralle0ed7402014-03-19 19:08:39 -07001576skip_device:
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001577 if (strcmp(image_fn, "-") == 0)
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001578 fd = 1;
1579 else {
JP Abgralle0ed7402014-03-19 19:08:39 -07001580 int o_flags = O_CREAT|O_RDWR;
1581
1582 if (img_type != E2IMAGE_RAW)
1583 o_flags |= O_TRUNC;
1584 if (access(image_fn, F_OK) != 0)
1585 flags |= E2IMAGE_CHECK_ZERO_FLAG;
1586 fd = ext2fs_open_file(image_fn, o_flags, 0600);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001587 if (fd < 0) {
1588 com_err(program_name, errno,
JP Abgralle0ed7402014-03-19 19:08:39 -07001589 _("while trying to open %s"), image_fn);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001590 exit(1);
1591 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001592 }
JP Abgralle0ed7402014-03-19 19:08:39 -07001593 if (dest_offset)
1594 seek_set(fd, dest_offset);
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001595
JP Abgralle0ed7402014-03-19 19:08:39 -07001596 if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) {
1597 com_err(program_name, 0, _("QCOW2 image can not be written to "
1598 "the stdout!\n"));
1599 exit(1);
1600 }
1601 if (fd != 1) {
1602 if (fstat(fd, &st)) {
1603 com_err(program_name, 0, "Can not stat output\n");
1604 exit(1);
1605 }
1606 if (S_ISBLK(st.st_mode))
1607 output_is_blk = 1;
1608 }
1609 if (flags & E2IMAGE_IS_QCOW2_FLAG) {
1610 ret = qcow2_write_raw_image(qcow2_fd, fd, header);
1611 if (ret) {
1612 if (ret == -QCOW_COMPRESSED)
1613 fprintf(stderr, _("Image (%s) is compressed\n"),
1614 image_fn);
1615 if (ret == -QCOW_ENCRYPTED)
1616 fprintf(stderr, _("Image (%s) is encrypted\n"),
1617 image_fn);
1618 com_err(program_name, ret,
1619 _("while trying to convert qcow2 image"
1620 " (%s) into raw image (%s)"),
1621 device_name, image_fn);
1622 }
1623 goto out;
1624 }
1625
1626 if (check) {
1627 if (img_type != E2IMAGE_RAW) {
1628 fprintf(stderr, _("The -c option only supported "
1629 "in raw mode\n"));
1630 exit(1);
1631 }
1632 if (fd == 1) {
1633 fprintf(stderr, _("The -c option is not supported "
1634 "when writing to stdout\n"));
1635 exit(1);
1636 }
1637 retval = ext2fs_get_mem(fs->blocksize, &check_buf);
1638 if (retval) {
1639 com_err(program_name, retval,
1640 _("while allocating check_buf"));
1641 exit(1);
1642 }
1643 }
1644 if (show_progress && (img_type != E2IMAGE_RAW)) {
1645 fprintf(stderr, _("The -p option only supported "
1646 "in raw mode\n"));
1647 exit(1);
1648 }
1649 if (img_type)
1650 write_raw_image_file(fs, fd, img_type, flags);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001651 else
1652 write_image_file(fs, fd);
Theodore Ts'oc5423c52001-02-08 05:45:17 +00001653
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001654 ext2fs_close (fs);
JP Abgralle0ed7402014-03-19 19:08:39 -07001655 if (check)
1656 printf(_("%d blocks already contained the data to be copied.\n"),
1657 skipped_blocks);
1658
Shay Nachmani03509c92015-01-26 16:59:32 +02001659 block_list_file=fopen(argv[optind+2], "w");
1660 if (NULL == block_list_file){
1661 exit(1);
1662 }
1663
1664 written_objects = fwrite(&meta_data_block_list, sizeof(blk_list),1, block_list_file);
1665 if(written_objects < 1){
1666 exit(1);
1667 }
1668
1669 fclose(block_list_file);
1670
JP Abgralle0ed7402014-03-19 19:08:39 -07001671out:
1672 if (header)
1673 free(header);
1674 if (qcow2_fd)
1675 close(qcow2_fd);
Theodore Ts'oa6d83022006-12-26 03:38:07 -05001676 remove_error_table(&et_ext2_error_table);
JP Abgralle0ed7402014-03-19 19:08:39 -07001677 return ret;
Shay Nachmani03509c92015-01-26 16:59:32 +02001678
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001679}