]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/e2image.c
e2image: clean up gcc -Wall and sparse nits
[thirdparty/e2fsprogs.git] / misc / e2image.c
CommitLineData
72ed1264
TT
1/*
2 * e2image.c --- Program which writes an image file backing up
3 * critical metadata for the filesystem.
4 *
a3827953 5 * Copyright 2000, 2001 by Theodore Ts'o.
72ed1264
TT
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
6304baf2
TT
13#define _LARGEFILE_SOURCE
14#define _LARGEFILE64_SOURCE
15
d1154eb4 16#include "config.h"
72ed1264
TT
17#include <fcntl.h>
18#include <grp.h>
19#ifdef HAVE_GETOPT_H
20#include <getopt.h>
21#else
22extern char *optarg;
23extern int optind;
24#endif
25#include <pwd.h>
26#include <stdio.h>
f38cf3cb 27#ifdef HAVE_STDLIB_H
72ed1264 28#include <stdlib.h>
f38cf3cb 29#endif
72ed1264
TT
30#include <string.h>
31#include <time.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <sys/stat.h>
36#include <sys/types.h>
bf0449b1 37#include <assert.h>
ecc859b2 38#include <signal.h>
72ed1264 39
54c637d4 40#include "ext2fs/ext2_fs.h"
72ed1264
TT
41#include "ext2fs/ext2fs.h"
42#include "et/com_err.h"
43#include "uuid/uuid.h"
44#include "e2p/e2p.h"
45#include "ext2fs/e2image.h"
bf0449b1 46#include "ext2fs/qcow2.h"
72ed1264
TT
47
48#include "../version.h"
49#include "nls-enable.h"
50
bf0449b1
LC
51#define QCOW_OFLAG_COPIED (1LL << 63)
52
53
f404167d
TT
54static const char * program_name = "e2image";
55static char * device_name = NULL;
56static char all_data;
57static char output_is_blk;
0e51f5ae 58/* writing to blk device: don't skip zeroed blocks */
d28759b2
TT
59static blk64_t source_offset, dest_offset;
60static char move_mode;
61static char show_progress;
72ed1264 62
d28759b2 63static blk64_t align_offset(blk64_t offset, unsigned int n)
bf0449b1 64{
d28759b2 65 return (offset + n - 1) & ~((blk64_t) n - 1);
bf0449b1
LC
66}
67
68static int get_bits_from_size(size_t size)
69{
70 int res = 0;
71
72 if (size == 0)
73 return -1;
74
75 while (size != 1) {
76 /* Not a power of two */
77 if (size & 1)
78 return -1;
79
80 size >>= 1;
81 res++;
82 }
83 return res;
84}
85
72ed1264
TT
86static void usage(void)
87{
c40c7cef 88 fprintf(stderr, _("Usage: %s [-rsIQafp] [-o src_offset] [-O dest_offset] device image_file\n"),
d851ed39 89 program_name);
72ed1264
TT
90 exit (1);
91}
92
80fe25a2
TT
93static ext2_loff_t seek_relative(int fd, int offset)
94{
95 ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_CUR);
96 if (ret < 0) {
97 perror("seek_relative");
98 exit(1);
99 }
100 return ret;
101}
102
103static ext2_loff_t seek_set(int fd, ext2_loff_t offset)
104{
105 ext2_loff_t ret = ext2fs_llseek(fd, offset, SEEK_SET);
106 if (ret < 0) {
107 perror("seek_set");
108 exit(1);
109 }
110 return ret;
111}
112
bf0449b1 113static void generic_write(int fd, void *buf, int blocksize, blk64_t block)
72ed1264 114{
bf0449b1
LC
115 int count, free_buf = 0;
116 errcode_t err;
72ed1264 117
bf0449b1
LC
118 if (!blocksize)
119 return;
120
121 if (!buf) {
122 free_buf = 1;
123 err = ext2fs_get_arrayzero(1, blocksize, &buf);
124 if (err) {
125 com_err(program_name, err, "while allocating buffer");
126 exit(1);
127 }
095b459d
TT
128 }
129
bf0449b1
LC
130 count = write(fd, buf, blocksize);
131 if (count != blocksize) {
132 if (count == -1)
133 err = errno;
134 else
135 err = 0;
136
137 if (block)
138 com_err(program_name, err, "error writing block %llu",
139 block);
140 else
141 com_err(program_name, err, "error in write()");
142
72ed1264
TT
143 exit(1);
144 }
bf0449b1
LC
145 if (free_buf)
146 ext2fs_free_mem(&buf);
147}
efc6f628 148
bf0449b1
LC
149static void write_header(int fd, void *hdr, int hdr_size, int wrt_size)
150{
151 char *header_buf;
96367ad3 152 int ret;
efc6f628 153
bf0449b1
LC
154 /* Sanity check */
155 if (hdr_size > wrt_size) {
45ff69ff
AD
156 fprintf(stderr, "%s",
157 _("Error: header size is bigger than wrt_size\n"));
bf0449b1
LC
158 }
159
160 ret = ext2fs_get_mem(wrt_size, &header_buf);
161 if (ret) {
162 fputs(_("Couldn't allocate header buffer\n"), stderr);
72ed1264
TT
163 exit(1);
164 }
bf0449b1 165
80fe25a2 166 seek_set(fd, 0);
bf0449b1
LC
167 memset(header_buf, 0, wrt_size);
168
169 if (hdr)
170 memcpy(header_buf, hdr, hdr_size);
171
172 generic_write(fd, header_buf, wrt_size, 0);
173
174 ext2fs_free_mem(&header_buf);
72ed1264
TT
175}
176
6304baf2 177static void write_image_file(ext2_filsys fs, int fd)
72ed1264 178{
6304baf2
TT
179 struct ext2_image_hdr hdr;
180 struct stat st;
181 errcode_t retval;
72ed1264 182
4c2ae57d 183 write_header(fd, NULL, sizeof(struct ext2_image_hdr), fs->blocksize);
72ed1264 184 memset(&hdr, 0, sizeof(struct ext2_image_hdr));
6304baf2 185
80fe25a2 186 hdr.offset_super = seek_relative(fd, 0);
72ed1264
TT
187 retval = ext2fs_image_super_write(fs, fd, 0);
188 if (retval) {
45ff69ff
AD
189 com_err(program_name, retval, "%s",
190 _("while writing superblock"));
72ed1264
TT
191 exit(1);
192 }
efc6f628 193
80fe25a2 194 hdr.offset_inode = seek_relative(fd, 0);
6304baf2
TT
195 retval = ext2fs_image_inode_write(fs, fd,
196 (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
72ed1264 197 if (retval) {
45ff69ff
AD
198 com_err(program_name, retval, "%s",
199 _("while writing inode table"));
72ed1264
TT
200 exit(1);
201 }
efc6f628 202
80fe25a2 203 hdr.offset_blockmap = seek_relative(fd, 0);
72ed1264
TT
204 retval = ext2fs_image_bitmap_write(fs, fd, 0);
205 if (retval) {
45ff69ff
AD
206 com_err(program_name, retval, "%s",
207 _("while writing block bitmap"));
72ed1264
TT
208 exit(1);
209 }
210
80fe25a2 211 hdr.offset_inodemap = seek_relative(fd, 0);
72ed1264
TT
212 retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
213 if (retval) {
45ff69ff
AD
214 com_err(program_name, retval, "%s",
215 _("while writing inode bitmap"));
72ed1264
TT
216 exit(1);
217 }
c5423c5b
TT
218
219 hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
220 strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
221 gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
df200fff 222 strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1);
095b459d
TT
223 hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
224 hdr.fs_blocksize = fs->blocksize;
efc6f628 225
c5423c5b
TT
226 if (stat(device_name, &st) == 0)
227 hdr.fs_device = st.st_rdev;
228
229 if (fstat(fd, &st) == 0) {
230 hdr.image_device = st.st_dev;
231 hdr.image_inode = st.st_ino;
232 }
233 memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
234
235 hdr.image_time = time(0);
4c2ae57d 236 write_header(fd, &hdr, sizeof(struct ext2_image_hdr), fs->blocksize);
6304baf2
TT
237}
238
239/*
240 * These set of functions are used to write a RAW image file.
241 */
f404167d
TT
242static ext2fs_block_bitmap meta_block_map;
243static ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */
244static blk64_t meta_blocks_count;
6304baf2
TT
245
246struct process_block_struct {
247 ext2_ino_t ino;
d851ed39 248 int is_dir;
6304baf2
TT
249};
250
251/*
252 * These subroutines short circuits ext2fs_get_blocks and
253 * ext2fs_check_directory; we use them since we already have the inode
254 * structure, so there's no point in letting the ext2fs library read
255 * the inode again.
256 */
257static ino_t stashed_ino = 0;
258static struct ext2_inode *stashed_inode;
259
efc6f628 260static errcode_t meta_get_blocks(ext2_filsys fs EXT2FS_ATTR((unused)),
54434927
TT
261 ext2_ino_t ino,
262 blk_t *blocks)
6304baf2
TT
263{
264 int i;
efc6f628 265
6304baf2
TT
266 if ((ino != stashed_ino) || !stashed_inode)
267 return EXT2_ET_CALLBACK_NOTHANDLED;
268
269 for (i=0; i < EXT2_N_BLOCKS; i++)
270 blocks[i] = stashed_inode->i_block[i];
271 return 0;
272}
273
efc6f628 274static errcode_t meta_check_directory(ext2_filsys fs EXT2FS_ATTR((unused)),
54434927 275 ext2_ino_t ino)
4ea7bd04
TT
276{
277 if ((ino != stashed_ino) || !stashed_inode)
278 return EXT2_ET_CALLBACK_NOTHANDLED;
279
280 if (!LINUX_S_ISDIR(stashed_inode->i_mode))
281 return EXT2_ET_NO_DIRECTORY;
282 return 0;
283}
284
efc6f628 285static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
54434927 286 ext2_ino_t ino,
6304baf2
TT
287 struct ext2_inode *inode)
288{
289 if ((ino != stashed_ino) || !stashed_inode)
290 return EXT2_ET_CALLBACK_NOTHANDLED;
291 *inode = *stashed_inode;
292 return 0;
293}
294
2d2abcc6 295static void use_inode_shortcuts(ext2_filsys fs, int use_shortcuts)
4ea7bd04 296{
2d2abcc6 297 if (use_shortcuts) {
4ea7bd04
TT
298 fs->get_blocks = meta_get_blocks;
299 fs->check_directory = meta_check_directory;
300 fs->read_inode = meta_read_inode;
301 stashed_ino = 0;
302 } else {
303 fs->get_blocks = 0;
304 fs->check_directory = 0;
305 fs->read_inode = 0;
306 }
307}
308
efc6f628 309static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
d991bc74 310 blk64_t *block_nr,
efc6f628 311 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
d991bc74 312 blk64_t ref_block EXT2FS_ATTR((unused)),
efc6f628 313 int ref_offset EXT2FS_ATTR((unused)),
54434927 314 void *priv_data EXT2FS_ATTR((unused)))
6304baf2 315{
d851ed39
TT
316 struct process_block_struct *p;
317
318 p = (struct process_block_struct *) priv_data;
319
3c041a51 320 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
bf0449b1 321 meta_blocks_count++;
efc6f628 322 if (scramble_block_map && p->is_dir && blockcnt >= 0)
3c041a51 323 ext2fs_mark_block_bitmap2(scramble_block_map, *block_nr);
6304baf2
TT
324 return 0;
325}
326
efc6f628 327static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)),
d991bc74 328 blk64_t *block_nr,
efc6f628 329 e2_blkcnt_t blockcnt,
d991bc74 330 blk64_t ref_block EXT2FS_ATTR((unused)),
efc6f628 331 int ref_offset EXT2FS_ATTR((unused)),
54434927 332 void *priv_data EXT2FS_ATTR((unused)))
6304baf2 333{
0e51f5ae 334 if (blockcnt < 0 || all_data) {
3c041a51 335 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
bf0449b1 336 meta_blocks_count++;
6304baf2
TT
337 }
338 return 0;
339}
340
341static void mark_table_blocks(ext2_filsys fs)
342{
d991bc74 343 blk64_t first_block, b;
54434927 344 unsigned int i,j;
efc6f628 345
bb1a46a4 346 first_block = fs->super->s_first_data_block;
6304baf2
TT
347 /*
348 * Mark primary superblock
349 */
3c041a51 350 ext2fs_mark_block_bitmap2(meta_block_map, first_block);
bf0449b1 351 meta_blocks_count++;
efc6f628 352
6304baf2
TT
353 /*
354 * Mark the primary superblock descriptors
355 */
356 for (j = 0; j < fs->desc_blocks; j++) {
3c041a51 357 ext2fs_mark_block_bitmap2(meta_block_map,
d991bc74 358 ext2fs_descriptor_block_loc2(fs, first_block, j));
6304baf2 359 }
bf0449b1 360 meta_blocks_count += fs->desc_blocks;
6304baf2
TT
361
362 for (i = 0; i < fs->group_desc_count; i++) {
363 /*
364 * Mark the blocks used for the inode table
365 */
0e51f5ae
PS
366 if ((output_is_blk ||
367 !ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT)) &&
6c9ce8a9
PS
368 ext2fs_inode_table_loc(fs, i)) {
369 unsigned int end = (unsigned) fs->inode_blocks_per_group;
370 /* skip unused blocks */
0e51f5ae
PS
371 if (!output_is_blk &&
372 EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
6c9ce8a9
PS
373 EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
374 end -= (ext2fs_bg_itable_unused(fs, i) /
375 EXT2_INODES_PER_BLOCK(fs->super));
d7cca6b0 376 for (j = 0, b = ext2fs_inode_table_loc(fs, i);
6c9ce8a9
PS
377 j < end;
378 j++, b++) {
3c041a51 379 ext2fs_mark_block_bitmap2(meta_block_map, b);
6c9ce8a9
PS
380 meta_blocks_count++;
381 }
6304baf2 382 }
efc6f628 383
6304baf2 384 /*
efc6f628 385 * Mark block used for the block bitmap
6304baf2 386 */
6c9ce8a9
PS
387 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) &&
388 ext2fs_block_bitmap_loc(fs, i)) {
3c041a51 389 ext2fs_mark_block_bitmap2(meta_block_map,
d7cca6b0 390 ext2fs_block_bitmap_loc(fs, i));
bf0449b1 391 meta_blocks_count++;
6304baf2 392 }
efc6f628 393
6304baf2 394 /*
efc6f628 395 * Mark block used for the inode bitmap
6304baf2 396 */
6c9ce8a9
PS
397 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) &&
398 ext2fs_inode_bitmap_loc(fs, i)) {
3c041a51 399 ext2fs_mark_block_bitmap2(meta_block_map,
d7cca6b0 400 ext2fs_inode_bitmap_loc(fs, i));
bf0449b1 401 meta_blocks_count++;
6304baf2 402 }
6304baf2
TT
403 }
404}
405
406/*
407 * This function returns 1 if the specified block is all zeros
408 */
409static int check_zero_block(char *buf, int blocksize)
410{
411 char *cp = buf;
412 int left = blocksize;
413
0e51f5ae
PS
414 if (output_is_blk)
415 return 0;
6304baf2
TT
416 while (left > 0) {
417 if (*cp++)
418 return 0;
419 left--;
420 }
421 return 1;
422}
423
f404167d 424static int name_id[256];
d851ed39 425
8a480350
TT
426#define EXT4_MAX_REC_LEN ((1<<16)-1)
427
d991bc74 428static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf)
d851ed39
TT
429{
430 char *p, *end, *cp;
431 struct ext2_dir_entry_2 *dirent;
8a480350
TT
432 unsigned int rec_len;
433 int id, len;
d851ed39 434
d851ed39
TT
435 end = buf + fs->blocksize;
436 for (p = buf; p < end-8; p += rec_len) {
437 dirent = (struct ext2_dir_entry_2 *) p;
438 rec_len = dirent->rec_len;
2eae0930
TT
439#ifdef WORDS_BIGENDIAN
440 rec_len = ext2fs_swab16(rec_len);
d851ed39 441#endif
8a480350
TT
442 if (rec_len == EXT4_MAX_REC_LEN || rec_len == 0)
443 rec_len = fs->blocksize;
444 else
445 rec_len = (rec_len & 65532) | ((rec_len & 3) << 16);
d851ed39
TT
446#if 0
447 printf("rec_len = %d, name_len = %d\n", rec_len, dirent->name_len);
448#endif
449 if (rec_len < 8 || (rec_len % 4) ||
450 (p+rec_len > end)) {
d28759b2
TT
451 printf("Corrupt directory block %llu: "
452 "bad rec_len (%d)\n", (unsigned long long) blk,
9b9a780f 453 rec_len);
d851ed39 454 rec_len = end - p;
8a480350
TT
455 (void) ext2fs_set_rec_len(fs, rec_len,
456 (struct ext2_dir_entry *) dirent);
2eae0930 457#ifdef WORDS_BIGENDIAN
8a480350 458 dirent->rec_len = ext2fs_swab16(dirent->rec_len);
d851ed39
TT
459#endif
460 continue;
461 }
577c773a 462 if (dirent->name_len + 8U > rec_len) {
d28759b2
TT
463 printf("Corrupt directory block %llu: "
464 "bad name_len (%d)\n", (unsigned long long) blk,
9b9a780f 465 dirent->name_len);
d851ed39
TT
466 dirent->name_len = rec_len - 8;
467 continue;
468 }
d851ed39 469 cp = p+8;
d851ed39
TT
470 len = rec_len - dirent->name_len - 8;
471 if (len > 0)
472 memset(cp+dirent->name_len, 0, len);
79fc2a99
TT
473 if (dirent->name_len==1 && cp[0] == '.')
474 continue;
475 if (dirent->name_len==2 && cp[0] == '.' && cp[1] == '.')
476 continue;
477
478 memset(cp, 'A', dirent->name_len);
d851ed39
TT
479 len = dirent->name_len;
480 id = name_id[len]++;
481 while ((len > 0) && (id > 0)) {
482 *cp += id % 26;
483 id = id / 26;
484 cp++;
485 len--;
486 }
487 }
488}
489
ecc859b2
PS
490static char got_sigint;
491
d28759b2 492static void sigint_handler(int unused EXT2FS_ATTR((unused)))
ecc859b2
PS
493{
494 got_sigint = 1;
495 signal (SIGINT, SIG_DFL);
496}
497
4ea7bd04 498static void output_meta_data_blocks(ext2_filsys fs, int fd)
6304baf2
TT
499{
500 errcode_t retval;
d991bc74 501 blk64_t blk;
d851ed39 502 char *buf, *zero_buf;
e3ef3502 503 int sparse = 0;
22b83f62
PS
504 blk64_t start = 0;
505 blk64_t distance = 0;
506 blk64_t end = ext2fs_blocks_count(fs->super);
d28759b2
TT
507 time_t last_update = 0;
508 time_t start_time = 0;
c40c7cef 509 blk64_t total_written = 0;
d28759b2 510 int bscount = 0;
6304baf2 511
bf0449b1
LC
512 retval = ext2fs_get_mem(fs->blocksize, &buf);
513 if (retval) {
514 com_err(program_name, retval, "while allocating buffer");
d851ed39
TT
515 exit(1);
516 }
bf0449b1
LC
517 retval = ext2fs_get_memzero(fs->blocksize, &zero_buf);
518 if (retval) {
519 com_err(program_name, retval, "while allocating buffer");
d851ed39
TT
520 exit(1);
521 }
c40c7cef
PS
522 if (show_progress) {
523 printf("Copying ");
524 bscount = printf("%llu / %llu blocks (%llu%%)",
525 total_written,
526 meta_blocks_count,
527 (total_written + 50) / ((meta_blocks_count + 50) / 100));
528 fflush(stdout);
529 last_update = time(NULL);
530 start_time = time(NULL);
531 }
22b83f62
PS
532 /* when doing an in place move to the right, you can't start
533 at the beginning or you will overwrite data, so instead
534 divide the fs up into distance size chunks and write them
535 in reverse. */
536 if (move_mode && dest_offset > source_offset) {
537 distance = (dest_offset - source_offset) / fs->blocksize;
538 if (distance < ext2fs_blocks_count(fs->super))
539 start = ext2fs_blocks_count(fs->super) - distance;
540 }
ecc859b2
PS
541 if (move_mode)
542 signal (SIGINT, sigint_handler);
22b83f62
PS
543more_blocks:
544 if (distance)
80fe25a2 545 seek_set(fd, (start * fs->blocksize) + dest_offset);
22b83f62 546 for (blk = start; blk < end; blk++) {
ecc859b2
PS
547 if (got_sigint) {
548 if (distance) {
549 /* moving to the right */
550 if (distance >= ext2fs_blocks_count(fs->super) ||
551 start == ext2fs_blocks_count(fs->super) - distance)
552 kill (getpid(), SIGINT);
553 } else {
554 /* moving to the left */
555 if (blk < (source_offset - dest_offset) / fs->blocksize)
556 kill (getpid(), SIGINT);
557 }
558 if (show_progress)
559 printf ("\r");
560 printf ("Stopping now will destroy the filesystem, "
561 "interrupt again if you are sure\n");
562 if (show_progress) {
563 printf("Copying ");
564 bscount = printf("%llu / %llu blocks (%llu%%)",
565 total_written,
566 meta_blocks_count,
567 (total_written + 50) / ((meta_blocks_count + 50)
568 / 100));
569 fflush(stdout);
570 }
571
572 got_sigint = 0;
573 }
c40c7cef 574 if (show_progress && last_update != time(NULL)) {
d28759b2 575 time_t duration;
c40c7cef
PS
576 last_update = time(NULL);
577 while (bscount--)
578 printf("\b");
579 bscount = printf("%llu / %llu blocks (%llu%%)",
580 total_written,
581 meta_blocks_count,
582 (total_written + 50) /
583 ((meta_blocks_count + 50) / 100));
d28759b2 584 duration = time(NULL) - start_time;
c40c7cef
PS
585 if (duration > 5) {
586 time_t est = (duration *
587 meta_blocks_count / total_written) -
588 (duration);
589 char buff[30];
590 strftime(buff, 30, "%T", gmtime(&est));
591 bscount += printf(" %s remaining at %.2f MB/s",
592 buff,
593 ((float)total_written /
594 ((1024 * 1024) / fs->blocksize)) /
595 duration);
596 }
597 fflush (stdout);
598 }
6304baf2 599 if ((blk >= fs->super->s_first_data_block) &&
3c041a51 600 ext2fs_test_block_bitmap2(meta_block_map, blk)) {
24a117ab 601 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
6304baf2
TT
602 if (retval) {
603 com_err(program_name, retval,
d991bc74 604 "error reading block %llu", blk);
6304baf2 605 }
c40c7cef 606 total_written++;
efc6f628 607 if (scramble_block_map &&
3c041a51 608 ext2fs_test_block_bitmap2(scramble_block_map, blk))
d851ed39 609 scramble_dir_block(fs, blk, buf);
6304baf2
TT
610 if ((fd != 1) && check_zero_block(buf, fs->blocksize))
611 goto sparse_write;
80fe25a2
TT
612 if (sparse)
613 seek_relative(fd, sparse);
614 generic_write(fd, buf, fs->blocksize, blk);
e3ef3502 615 sparse = 0;
6304baf2
TT
616 } else {
617 sparse_write:
618 if (fd == 1) {
80fe25a2 619 generic_write(fd, zero_buf, fs->blocksize, blk);
6304baf2
TT
620 continue;
621 }
e3ef3502 622 sparse += fs->blocksize;
2b7a30cc 623 if (sparse > 1024*1024) {
80fe25a2 624 seek_relative(fd, 1024*1024);
2b7a30cc 625 sparse -= 1024*1024;
e3ef3502 626 }
6304baf2
TT
627 }
628 }
22b83f62
PS
629 if (distance && start) {
630 if (start < distance) {
631 end = start;
632 start = 0;
633 } else {
634 end -= distance;
635 start -= distance;
636 if (end < distance) {
637 /* past overlap, do rest in one go */
638 end = start;
639 start = 0;
640 }
641 }
642 sparse = 0;
643 goto more_blocks;
644 }
ecc859b2 645 signal (SIGINT, SIG_DFL);
c40c7cef 646 if (show_progress) {
c40c7cef
PS
647 time_t duration = time(NULL) - start_time;
648 char buff[30];
d28759b2
TT
649 while (bscount--)
650 printf("\b");
c40c7cef
PS
651 strftime(buff, 30, "%T", gmtime(&duration));
652 printf("\b\b\b\b\b\b\b\bCopied %llu / %llu blocks (%llu%%) in "
653 "%s at %.2f MB/s \n",
654 total_written,
655 meta_blocks_count,
656 (total_written + 50) / ((meta_blocks_count + 50) / 100),
657 buff,
658 ((float)total_written /
659 ((1024 * 1024) / fs->blocksize)) /
660 duration);
661
662 }
50a676e9
TT
663#ifdef HAVE_FTRUNCATE64
664 if (sparse) {
22b83f62
PS
665 ext2_loff_t offset;
666 if (distance)
80fe25a2
TT
667 offset = seek_set(fd,
668 fs->blocksize * ext2fs_blocks_count(fs->super) + dest_offset);
669 else
670 offset = seek_relative(fd, sparse);
50a676e9 671
80fe25a2
TT
672 if (ftruncate64(fd, offset) < 0) {
673 seek_relative(fd, -1);
674 generic_write(fd, zero_buf, 1, -1);
675 }
50a676e9
TT
676 }
677#else
80fe25a2
TT
678 if (sparse && !distance) {
679 seek_relative(fd, sparse-1);
680 generic_write(fd, zero_buf, 1, -1);
681 }
50a676e9 682#endif
bf0449b1
LC
683 ext2fs_free_mem(&zero_buf);
684 ext2fs_free_mem(&buf);
6304baf2
TT
685}
686
e64e6761 687static void init_l1_table(struct ext2_qcow2_image *image)
bf0449b1
LC
688{
689 __u64 *l1_table;
690 errcode_t ret;
691
692 ret = ext2fs_get_arrayzero(image->l1_size, sizeof(__u64), &l1_table);
693 if (ret) {
694 com_err(program_name, ret, "while allocating l1 table");
695 exit(1);
696 }
697
698 image->l1_table = l1_table;
699}
700
701static void init_l2_cache(struct ext2_qcow2_image *image)
702{
703 unsigned int count, i;
704 struct ext2_qcow2_l2_cache *cache;
705 struct ext2_qcow2_l2_table *table;
706 errcode_t ret;
707
708 ret = ext2fs_get_arrayzero(1, sizeof(struct ext2_qcow2_l2_cache),
709 &cache);
710 if (ret)
711 goto alloc_err;
712
713 count = (image->l1_size > L2_CACHE_PREALLOC) ? L2_CACHE_PREALLOC :
714 image->l1_size;
715
716 cache->count = count;
717 cache->free = count;
718 cache->next_offset = image->l2_offset;
719
720 for (i = 0; i < count; i++) {
721 ret = ext2fs_get_arrayzero(1,
722 sizeof(struct ext2_qcow2_l2_table), &table);
723 if (ret)
724 goto alloc_err;
725
726 ret = ext2fs_get_arrayzero(image->l2_size,
727 sizeof(__u64), &table->data);
728 if (ret)
729 goto alloc_err;
730
731 table->next = cache->free_head;
732 cache->free_head = table;
733 }
734
735 image->l2_cache = cache;
736 return;
737
738alloc_err:
739 com_err(program_name, ret, "while allocating l2 cache");
740 exit(1);
741}
742
743static void put_l2_cache(struct ext2_qcow2_image *image)
744{
745 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
746 struct ext2_qcow2_l2_table *tmp, *table;
747
748 if (!cache)
749 return;
750
751 table = cache->free_head;
752 cache->free_head = NULL;
753again:
754 while (table) {
755 tmp = table;
756 table = table->next;
757 ext2fs_free_mem(&tmp->data);
758 ext2fs_free_mem(&tmp);
759 }
760
761 if (cache->free != cache->count) {
762 fprintf(stderr, "Warning: There are still tables in the "
763 "cache while putting the cache, data will "
764 "be lost so the image may not be valid.\n");
765 table = cache->used_head;
766 cache->used_head = NULL;
767 goto again;
768 }
769
770 ext2fs_free_mem(&cache);
771}
772
773static int init_refcount(struct ext2_qcow2_image *img, blk64_t table_offset)
774{
775 struct ext2_qcow2_refcount *ref;
776 blk64_t table_clusters;
777 errcode_t ret;
778
779 ref = &(img->refcount);
780
781 /*
782 * One refcount block addresses 2048 clusters, one refcount table
783 * addresses cluster/sizeof(__u64) refcount blocks, and we need
784 * to address meta_blocks_count clusters + qcow2 metadata clusters
785 * in the worst case.
786 */
787 table_clusters = meta_blocks_count + (table_offset >>
788 img->cluster_bits);
789 table_clusters >>= (img->cluster_bits + 6 - 1);
790 table_clusters = (table_clusters == 0) ? 1 : table_clusters;
791
792 ref->refcount_table_offset = table_offset;
793 ref->refcount_table_clusters = table_clusters;
794 ref->refcount_table_index = 0;
795 ref->refcount_block_index = 0;
796
797 /* Allocate refcount table */
798 ret = ext2fs_get_arrayzero(ref->refcount_table_clusters,
799 img->cluster_size, &ref->refcount_table);
800 if (ret)
801 return ret;
802
803 /* Allocate refcount block */
804 ret = ext2fs_get_arrayzero(1, img->cluster_size, &ref->refcount_block);
805 if (ret)
806 ext2fs_free_mem(&ref->refcount_table);
807
808 return ret;
809}
810
811static int initialize_qcow2_image(int fd, ext2_filsys fs,
812 struct ext2_qcow2_image *image)
813{
814 struct ext2_qcow2_hdr *header;
815 blk64_t total_size, offset;
816 int shift, l2_bits, header_size, l1_size, ret;
817 int cluster_bits = get_bits_from_size(fs->blocksize);
818 struct ext2_super_block *sb = fs->super;
819
820 /* Allocate header */
821 ret = ext2fs_get_memzero(sizeof(struct ext2_qcow2_hdr), &header);
822 if (ret)
823 return ret;
824
825 total_size = ext2fs_blocks_count(sb) << cluster_bits;
826 image->cluster_size = fs->blocksize;
827 image->l2_size = 1 << (cluster_bits - 3);
828 image->cluster_bits = cluster_bits;
829 image->fd = fd;
830
831 header->magic = ext2fs_cpu_to_be32(QCOW_MAGIC);
832 header->version = ext2fs_cpu_to_be32(QCOW_VERSION);
833 header->size = ext2fs_cpu_to_be64(total_size);
834 header->cluster_bits = ext2fs_cpu_to_be32(cluster_bits);
835
836 header_size = (sizeof(struct ext2_qcow2_hdr) + 7) & ~7;
837 offset = align_offset(header_size, image->cluster_size);
838
839 header->l1_table_offset = ext2fs_cpu_to_be64(offset);
840 image->l1_offset = offset;
841
842 l2_bits = cluster_bits - 3;
843 shift = cluster_bits + l2_bits;
844 l1_size = ((total_size + (1LL << shift) - 1) >> shift);
845 header->l1_size = ext2fs_cpu_to_be32(l1_size);
846 image->l1_size = l1_size;
847
848 /* Make space for L1 table */
849 offset += align_offset(l1_size * sizeof(blk64_t), image->cluster_size);
850
851 /* Initialize refcounting */
852 ret = init_refcount(image, offset);
853 if (ret) {
854 ext2fs_free_mem(&header);
855 return ret;
856 }
857 header->refcount_table_offset = ext2fs_cpu_to_be64(offset);
858 header->refcount_table_clusters =
859 ext2fs_cpu_to_be32(image->refcount.refcount_table_clusters);
860 offset += image->cluster_size;
861 offset += image->refcount.refcount_table_clusters <<
862 image->cluster_bits;
863
864 /* Make space for L2 tables */
865 image->l2_offset = offset;
866 offset += image->cluster_size;
867
868 /* Make space for first refcount block */
869 image->refcount.refcount_block_offset = offset;
870
871 image->hdr = header;
872 /* Initialize l1 and l2 tables */
e64e6761 873 init_l1_table(image);
bf0449b1
LC
874 init_l2_cache(image);
875
876 return 0;
877}
878
879static void free_qcow2_image(struct ext2_qcow2_image *img)
880{
bf0449b1
LC
881 if (!img)
882 return;
883
884 if (img->hdr)
885 ext2fs_free_mem(&img->hdr);
886
887 if (img->l1_table)
888 ext2fs_free_mem(&img->l1_table);
889
890 if (img->refcount.refcount_table)
891 ext2fs_free_mem(&img->refcount.refcount_table);
892 if (img->refcount.refcount_block)
893 ext2fs_free_mem(&img->refcount.refcount_block);
894
895 put_l2_cache(img);
896
897 ext2fs_free_mem(&img);
898}
899
900/**
901 * Put table from used list (used_head) into free list (free_head).
902 * l2_table is used to return pointer to the next used table (used_head).
903 */
904static void put_used_table(struct ext2_qcow2_image *img,
905 struct ext2_qcow2_l2_table **l2_table)
906{
907 struct ext2_qcow2_l2_cache *cache = img->l2_cache;
908 struct ext2_qcow2_l2_table *table;
909
910 table = cache->used_head;
911 cache->used_head = table->next;
912
913 assert(table);
914 if (!table->next)
915 cache->used_tail = NULL;
916
917 /* Clean the table for case we will need to use it again */
918 memset(table->data, 0, img->cluster_size);
919 table->next = cache->free_head;
920 cache->free_head = table;
921
922 cache->free++;
923
924 *l2_table = cache->used_head;
925}
926
927static void flush_l2_cache(struct ext2_qcow2_image *image)
928{
9d10f017
LC
929 blk64_t seek = 0;
930 ext2_loff_t offset;
bf0449b1
LC
931 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
932 struct ext2_qcow2_l2_table *table = cache->used_head;
933 int fd = image->fd;
934
935 /* Store current position */
80fe25a2 936 offset = seek_relative(fd, 0);
bf0449b1
LC
937
938 assert(table);
939 while (cache->free < cache->count) {
940 if (seek != table->offset) {
80fe25a2 941 seek_set(fd, table->offset);
bf0449b1
LC
942 seek = table->offset;
943 }
944
945 generic_write(fd, (char *)table->data, image->cluster_size , 0);
946 put_used_table(image, &table);
947 seek += image->cluster_size;
948 }
949
950 /* Restore previous position */
80fe25a2 951 seek_set(fd, offset);
bf0449b1
LC
952}
953
954/**
955 * Get first free table (from free_head) and put it into tail of used list
956 * (to used_tail).
957 * l2_table is used to return pointer to moved table.
958 * Returns 1 if the cache is full, 0 otherwise.
959 */
960static void get_free_table(struct ext2_qcow2_image *image,
961 struct ext2_qcow2_l2_table **l2_table)
962{
963 struct ext2_qcow2_l2_table *table;
964 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
965
966 if (0 == cache->free)
967 flush_l2_cache(image);
968
969 table = cache->free_head;
970 assert(table);
971 cache->free_head = table->next;
972
973 if (cache->used_tail)
974 cache->used_tail->next = table;
975 else
976 /* First item in the used list */
977 cache->used_head = table;
978
979 cache->used_tail = table;
980 cache->free--;
981
982 *l2_table = table;
983}
984
985static int add_l2_item(struct ext2_qcow2_image *img, blk64_t blk,
986 blk64_t data, blk64_t next)
987{
988 struct ext2_qcow2_l2_cache *cache = img->l2_cache;
989 struct ext2_qcow2_l2_table *table = cache->used_tail;
990 blk64_t l1_index = blk / img->l2_size;
991 blk64_t l2_index = blk & (img->l2_size - 1);
992 int ret = 0;
993
994 /*
995 * Need to create new table if it does not exist,
996 * or if it is full
997 */
998 if (!table || (table->l1_index != l1_index)) {
999 get_free_table(img, &table);
1000 table->l1_index = l1_index;
1001 table->offset = cache->next_offset;
1002 cache->next_offset = next;
1003 img->l1_table[l1_index] =
1004 ext2fs_cpu_to_be64(table->offset | QCOW_OFLAG_COPIED);
1005 ret++;
1006 }
1007
1008 table->data[l2_index] = ext2fs_cpu_to_be64(data | QCOW_OFLAG_COPIED);
1009 return ret;
1010}
1011
1012static int update_refcount(int fd, struct ext2_qcow2_image *img,
1013 blk64_t offset, blk64_t rfblk_pos)
1014{
1015 struct ext2_qcow2_refcount *ref;
1016 __u32 table_index;
1017 int ret = 0;
1018
1019 ref = &(img->refcount);
1020 table_index = offset >> (2 * img->cluster_bits - 1);
1021
1022 /*
1023 * Need to create new refcount block when the offset addresses
1024 * another item in the refcount table
1025 */
1026 if (table_index != ref->refcount_table_index) {
1027
80fe25a2 1028 seek_set(fd, ref->refcount_block_offset);
bf0449b1
LC
1029
1030 generic_write(fd, (char *)ref->refcount_block,
1031 img->cluster_size, 0);
1032 memset(ref->refcount_block, 0, img->cluster_size);
1033
1034 ref->refcount_table[ref->refcount_table_index] =
1035 ext2fs_cpu_to_be64(ref->refcount_block_offset);
1036 ref->refcount_block_offset = rfblk_pos;
1037 ref->refcount_block_index = 0;
1038 ref->refcount_table_index = table_index;
1039 ret++;
1040 }
1041
1042 /*
1043 * We are relying on the fact that we are creating the qcow2
1044 * image sequentially, hence we will always allocate refcount
1045 * block items sequentialy.
1046 */
1047 ref->refcount_block[ref->refcount_block_index] = ext2fs_cpu_to_be16(1);
1048 ref->refcount_block_index++;
1049 return ret;
1050}
1051
1052static int sync_refcount(int fd, struct ext2_qcow2_image *img)
1053{
1054 struct ext2_qcow2_refcount *ref;
1055
1056 ref = &(img->refcount);
1057
1058 ref->refcount_table[ref->refcount_table_index] =
1059 ext2fs_cpu_to_be64(ref->refcount_block_offset);
80fe25a2 1060 seek_set(fd, ref->refcount_table_offset);
bf0449b1
LC
1061 generic_write(fd, (char *)ref->refcount_table,
1062 ref->refcount_table_clusters << img->cluster_bits, 0);
1063
80fe25a2 1064 seek_set(fd, ref->refcount_block_offset);
bf0449b1
LC
1065 generic_write(fd, (char *)ref->refcount_block, img->cluster_size, 0);
1066 return 0;
1067}
1068
1069static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd)
1070{
1071 errcode_t retval;
96367ad3 1072 blk64_t blk, offset, size, end;
bf0449b1 1073 char *buf;
bf0449b1 1074 struct ext2_qcow2_image *img;
96367ad3 1075 unsigned int header_size;
bf0449b1
LC
1076
1077 /* allocate struct ext2_qcow2_image */
1078 retval = ext2fs_get_mem(sizeof(struct ext2_qcow2_image), &img);
1079 if (retval) {
1080 com_err(program_name, retval,
1081 "while allocating ext2_qcow2_image");
1082 exit(1);
1083 }
1084
1085 retval = initialize_qcow2_image(fd, fs, img);
1086 if (retval) {
1087 com_err(program_name, retval,
1088 "while initializing ext2_qcow2_image");
1089 exit(1);
1090 }
1091 header_size = align_offset(sizeof(struct ext2_qcow2_hdr),
1092 img->cluster_size);
1093 write_header(fd, img->hdr, sizeof(struct ext2_qcow2_hdr), header_size);
1094
1095 /* Refcount all qcow2 related metadata up to refcount_block_offset */
1096 end = img->refcount.refcount_block_offset;
80fe25a2 1097 seek_set(fd, end);
bf0449b1
LC
1098 blk = end + img->cluster_size;
1099 for (offset = 0; offset <= end; offset += img->cluster_size) {
1100 if (update_refcount(fd, img, offset, blk)) {
1101 blk += img->cluster_size;
1102 /*
1103 * If we create new refcount block, we need to refcount
1104 * it as well.
1105 */
1106 end += img->cluster_size;
1107 }
1108 }
80fe25a2 1109 seek_set(fd, offset);
bf0449b1
LC
1110
1111 retval = ext2fs_get_mem(fs->blocksize, &buf);
1112 if (retval) {
1113 com_err(program_name, retval, "while allocating buffer");
1114 exit(1);
1115 }
1116 /* Write qcow2 data blocks */
1117 for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) {
1118 if ((blk >= fs->super->s_first_data_block) &&
1119 ext2fs_test_block_bitmap2(meta_block_map, blk)) {
1120 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
1121 if (retval) {
1122 com_err(program_name, retval,
1123 "error reading block %llu", blk);
1124 continue;
1125 }
1126 if (scramble_block_map &&
1127 ext2fs_test_block_bitmap2(scramble_block_map, blk))
1128 scramble_dir_block(fs, blk, buf);
1129 if (check_zero_block(buf, fs->blocksize))
1130 continue;
1131
1132 if (update_refcount(fd, img, offset, offset)) {
1133 /* Make space for another refcount block */
1134 offset += img->cluster_size;
80fe25a2 1135 seek_set(fd, offset);
bf0449b1
LC
1136 /*
1137 * We have created the new refcount block, this
1138 * means that we need to refcount it as well.
1139 * So the previous update_refcount refcounted
1140 * the block itself and now we are going to
1141 * create refcount for data. New refcount
1142 * block should not be created!
1143 */
1144 if (update_refcount(fd, img, offset, offset)) {
1145 fprintf(stderr, "Programming error: "
1146 "multiple sequential refcount "
1147 "blocks created!\n");
1148 exit(1);
1149 }
1150 }
1151
1152 generic_write(fd, buf, fs->blocksize, 0);
1153
1154 if (add_l2_item(img, blk, offset,
1155 offset + img->cluster_size)) {
1156 offset += img->cluster_size;
1157 if (update_refcount(fd, img, offset,
1158 offset + img->cluster_size)) {
1159 offset += img->cluster_size;
1160 if (update_refcount(fd, img, offset,
1161 offset)) {
1162 fprintf(stderr,
1163 "Programming error: multiple sequential refcount "
1164 "blocks created!\n");
1165 exit(1);
1166 }
1167 }
1168 offset += img->cluster_size;
80fe25a2 1169 seek_set(fd, offset);
bf0449b1
LC
1170 continue;
1171 }
1172
1173 offset += img->cluster_size;
1174 }
1175 }
1176 update_refcount(fd, img, offset, offset);
1177 flush_l2_cache(img);
1178 sync_refcount(fd, img);
1179
1180 /* Write l1_table*/
80fe25a2 1181 seek_set(fd, img->l1_offset);
bf0449b1
LC
1182 size = img->l1_size * sizeof(__u64);
1183 generic_write(fd, (char *)img->l1_table, size, 0);
1184
1185 ext2fs_free_mem(&buf);
1186 free_qcow2_image(img);
1187}
1188
1189static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
6304baf2
TT
1190{
1191 struct process_block_struct pb;
1192 struct ext2_inode inode;
1193 ext2_inode_scan scan;
1194 ext2_ino_t ino;
1195 errcode_t retval;
1196 char * block_buf;
efc6f628 1197
bf0449b1 1198 meta_blocks_count = 0;
6304baf2
TT
1199 retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
1200 &meta_block_map);
1201 if (retval) {
1202 com_err(program_name, retval, "while allocating block bitmap");
1203 exit(1);
1204 }
d851ed39 1205
bf0449b1 1206 if (flags & E2IMAGE_SCRAMBLE_FLAG) {
d851ed39
TT
1207 retval = ext2fs_allocate_block_bitmap(fs, "scramble block map",
1208 &scramble_block_map);
1209 if (retval) {
efc6f628 1210 com_err(program_name, retval,
d851ed39
TT
1211 "while allocating scramble block bitmap");
1212 exit(1);
1213 }
1214 }
efc6f628 1215
6304baf2 1216 mark_table_blocks(fs);
c40c7cef
PS
1217 if (show_progress)
1218 printf("Scanning inodes...\n");
6304baf2
TT
1219
1220 retval = ext2fs_open_inode_scan(fs, 0, &scan);
1221 if (retval) {
45ff69ff
AD
1222 com_err(program_name, retval,"%s",
1223 _("while opening inode scan"));
6304baf2
TT
1224 exit(1);
1225 }
1226
bf0449b1
LC
1227 retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
1228 if (retval) {
45ff69ff
AD
1229 com_err(program_name, 0, "%s",
1230 _("Can't allocate block buffer"));
6304baf2
TT
1231 exit(1);
1232 }
efc6f628 1233
4ea7bd04 1234 use_inode_shortcuts(fs, 1);
6304baf2
TT
1235 stashed_inode = &inode;
1236 while (1) {
1237 retval = ext2fs_get_next_inode(scan, &ino, &inode);
3432a916
TT
1238 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
1239 continue;
6304baf2 1240 if (retval) {
45ff69ff 1241 com_err(program_name, retval, "%s",
6304baf2
TT
1242 _("while getting next inode"));
1243 exit(1);
1244 }
1245 if (ino == 0)
1246 break;
ed909bbe
TT
1247 if (!inode.i_links_count)
1248 continue;
0c80c44b 1249 if (ext2fs_file_acl_block(fs, &inode)) {
3c041a51 1250 ext2fs_mark_block_bitmap2(meta_block_map,
0c80c44b 1251 ext2fs_file_acl_block(fs, &inode));
bf0449b1 1252 meta_blocks_count++;
ed909bbe 1253 }
0c80c44b 1254 if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
6304baf2 1255 continue;
efc6f628 1256
6304baf2 1257 stashed_ino = ino;
d851ed39
TT
1258 pb.ino = ino;
1259 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
1c1e0049 1260 if (LINUX_S_ISDIR(inode.i_mode) ||
eca53e3c 1261 (LINUX_S_ISLNK(inode.i_mode) &&
0c80c44b 1262 ext2fs_inode_has_valid_blocks2(fs, &inode)) ||
1c1e0049 1263 ino == fs->super->s_journal_inum) {
d991bc74 1264 retval = ext2fs_block_iterate3(fs, ino,
43323be9
TT
1265 BLOCK_FLAG_READ_ONLY, block_buf,
1266 process_dir_block, &pb);
6304baf2
TT
1267 if (retval) {
1268 com_err(program_name, retval,
d0ff90d5 1269 "while iterating over inode %u",
6304baf2
TT
1270 ino);
1271 exit(1);
1272 }
1273 } else {
43323be9
TT
1274 if ((inode.i_flags & EXT4_EXTENTS_FL) ||
1275 inode.i_block[EXT2_IND_BLOCK] ||
6304baf2 1276 inode.i_block[EXT2_DIND_BLOCK] ||
0e51f5ae 1277 inode.i_block[EXT2_TIND_BLOCK] || all_data) {
d991bc74 1278 retval = ext2fs_block_iterate3(fs,
43323be9 1279 ino, BLOCK_FLAG_READ_ONLY, block_buf,
6304baf2
TT
1280 process_file_block, &pb);
1281 if (retval) {
1282 com_err(program_name, retval,
d0ff90d5 1283 "while iterating over inode %u", ino);
6304baf2
TT
1284 exit(1);
1285 }
1286 }
6304baf2
TT
1287 }
1288 }
4ea7bd04 1289 use_inode_shortcuts(fs, 0);
bf0449b1
LC
1290
1291 if (type & E2IMAGE_QCOW2)
1292 output_qcow2_meta_data_blocks(fs, fd);
1293 else
1294 output_meta_data_blocks(fs, fd);
1295
1296 ext2fs_free_mem(&block_buf);
1297 ext2fs_close_inode_scan(scan);
1298 ext2fs_free_block_bitmap(meta_block_map);
1299 if (type & E2IMAGE_SCRAMBLE_FLAG)
1300 ext2fs_free_block_bitmap(scramble_block_map);
6304baf2
TT
1301}
1302
bf0449b1 1303static void install_image(char *device, char *image_fn, int type)
8c6b6483 1304{
8c6b6483
TT
1305 errcode_t retval;
1306 ext2_filsys fs;
e7f983bf 1307 int open_flag = EXT2_FLAG_IMAGE_FILE | EXT2_FLAG_64BITS;
8c6b6483
TT
1308 int fd = 0;
1309 io_manager io_ptr;
bf0449b1 1310 io_channel io;
8c6b6483 1311
bf0449b1
LC
1312 if (type) {
1313 com_err(program_name, 0, "Raw and qcow2 images cannot"
1314 "be installed");
8c6b6483
TT
1315 exit(1);
1316 }
efc6f628 1317
8c6b6483 1318#ifdef CONFIG_TESTIO_DEBUG
f38cf3cb
TT
1319 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
1320 io_ptr = test_io_manager;
1321 test_io_backing_manager = unix_io_manager;
1322 } else
8c6b6483 1323#endif
f38cf3cb 1324 io_ptr = unix_io_manager;
8c6b6483
TT
1325
1326 retval = ext2fs_open (image_fn, open_flag, 0, 0,
1327 io_ptr, &fs);
1328 if (retval) {
1329 com_err (program_name, retval, _("while trying to open %s"),
1330 image_fn);
1331 exit(1);
1332 }
1333
1334 retval = ext2fs_read_bitmaps (fs);
1335 if (retval) {
1336 com_err(program_name, retval, "error reading bitmaps");
1337 exit(1);
1338 }
1339
c4012e5a 1340 fd = ext2fs_open_file(image_fn, O_RDONLY, 0);
8c6b6483
TT
1341 if (fd < 0) {
1342 perror(image_fn);
1343 exit(1);
1344 }
1345
efc6f628 1346 retval = io_ptr->open(device, IO_FLAG_RW, &io);
8c6b6483 1347 if (retval) {
6e82cd7e 1348 com_err(device, 0, "while opening device file");
8c6b6483
TT
1349 exit(1);
1350 }
1351
8c6b6483
TT
1352 ext2fs_rewrite_to_io(fs, io);
1353
80fe25a2 1354 seek_set(fd, fs->image_header->offset_inode);
8c6b6483
TT
1355
1356 retval = ext2fs_image_inode_read(fs, fd, 0);
1357 if (retval) {
1358 com_err(image_fn, 0, "while restoring the image table");
1359 exit(1);
1360 }
1361
e39082e2 1362 close(fd);
8c6b6483 1363 ext2fs_close (fs);
8c6b6483
TT
1364}
1365
92dcfb76
LC
1366static struct ext2_qcow2_hdr *check_qcow2_image(int *fd, char *name)
1367{
1368
c859cb1d 1369 *fd = ext2fs_open_file(name, O_RDONLY, 0600);
92dcfb76
LC
1370 if (*fd < 0)
1371 return NULL;
1372
1373 return qcow2_read_header(*fd);
1374}
1375
6304baf2
TT
1376int main (int argc, char ** argv)
1377{
1378 int c;
1379 errcode_t retval;
1380 ext2_filsys fs;
d28759b2 1381 char *image_fn, offset_opt[64];
92dcfb76 1382 struct ext2_qcow2_hdr *header = NULL;
d991bc74 1383 int open_flag = EXT2_FLAG_64BITS;
bf0449b1
LC
1384 int img_type = 0;
1385 int flags = 0;
6c327e9c 1386 int mount_flags = 0;
92dcfb76 1387 int qcow2_fd = 0;
6304baf2 1388 int fd = 0;
92dcfb76 1389 int ret = 0;
6c327e9c 1390 int ignore_rw_mount = 0;
0e51f5ae 1391 struct stat st;
6304baf2
TT
1392
1393#ifdef ENABLE_NLS
1394 setlocale(LC_MESSAGES, "");
14308a53 1395 setlocale(LC_CTYPE, "");
6304baf2
TT
1396 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1397 textdomain(NLS_CAT_NAME);
9d4507c5 1398 set_com_err_gettext(gettext);
6304baf2 1399#endif
0f8973fb
TT
1400 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
1401 E2FSPROGS_DATE);
6304baf2
TT
1402 if (argc && *argv)
1403 program_name = *argv;
a6d8302b 1404 add_error_table(&et_ext2_error_table);
c40c7cef 1405 while ((c = getopt(argc, argv, "rsIQafo:O:p")) != EOF)
6304baf2 1406 switch (c) {
bf0449b1
LC
1407 case 'I':
1408 flags |= E2IMAGE_INSTALL_FLAG;
1409 break;
1410 case 'Q':
1411 if (img_type)
1412 usage();
1413 img_type |= E2IMAGE_QCOW2;
1414 break;
6304baf2 1415 case 'r':
bf0449b1
LC
1416 if (img_type)
1417 usage();
1418 img_type |= E2IMAGE_RAW;
6304baf2 1419 break;
d851ed39 1420 case 's':
bf0449b1 1421 flags |= E2IMAGE_SCRAMBLE_FLAG;
8c6b6483 1422 break;
0e51f5ae
PS
1423 case 'a':
1424 all_data = 1;
1425 break;
6c327e9c
CM
1426 case 'f':
1427 ignore_rw_mount = 1;
1428 break;
6b5535f4
PS
1429 case 'o':
1430 source_offset = strtoull(optarg, NULL, 0);
1431 break;
1432 case 'O':
1433 dest_offset = strtoull(optarg, NULL, 0);
1434 break;
c40c7cef
PS
1435 case 'p':
1436 show_progress = 1;
1437 break;
6304baf2
TT
1438 default:
1439 usage();
1440 }
22b83f62
PS
1441 if (optind == argc - 1 &&
1442 (source_offset || dest_offset))
1443 move_mode = 1;
1444 else if (optind != argc - 2 )
6304baf2 1445 usage();
fdaf34f8
TR
1446
1447 if (all_data && !img_type) {
1448 com_err(program_name, 0, "-a option can only be used "
1449 "with raw or QCOW2 images.");
1450 exit(1);
1451 }
6b5535f4
PS
1452 if ((source_offset || dest_offset) && img_type != E2IMAGE_RAW) {
1453 com_err(program_name, 0,
1454 "Offsets are only allowed with raw images.");
1455 exit(1);
1456 }
22b83f62
PS
1457 if (move_mode && img_type != E2IMAGE_RAW) {
1458 com_err(program_name, 0,
1459 "Move mode is only allowed with raw images.");
1460 exit(1);
1461 }
1462 if (move_mode && !all_data) {
1463 com_err(program_name, 0,
1464 "Move mode requires all data mode.");
1465 exit(1);
1466 }
6304baf2 1467 device_name = argv[optind];
22b83f62
PS
1468 if (move_mode)
1469 image_fn = device_name;
1470 else image_fn = argv[optind+1];
8c6b6483 1471
b3993825
DW
1472 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
1473 if (retval) {
1474 com_err(program_name, retval, "checking if mounted");
1475 exit(1);
1476 }
6c327e9c
CM
1477
1478 if (img_type && !ignore_rw_mount &&
1479 (mount_flags & EXT2_MF_MOUNTED) &&
1480 !(mount_flags & EXT2_MF_READONLY)) {
1481 fprintf(stderr, "\nRunning e2image on a R/W mounted "
1482 "filesystem can result in an\n"
1483 "inconsistent image which will not be useful "
1484 "for debugging purposes.\n"
1485 "Use -f option if you really want to do that.\n");
1486 exit(1);
1487 }
1488
bf0449b1
LC
1489 if (flags & E2IMAGE_INSTALL_FLAG) {
1490 install_image(device_name, image_fn, img_type);
6e82cd7e 1491 exit (0);
8c6b6483
TT
1492 }
1493
92dcfb76
LC
1494 if (img_type & E2IMAGE_RAW) {
1495 header = check_qcow2_image(&qcow2_fd, device_name);
1496 if (header) {
1497 flags |= E2IMAGE_IS_QCOW2_FLAG;
1498 goto skip_device;
1499 }
1500 }
d28759b2
TT
1501 sprintf(offset_opt, "offset=%llu", source_offset);
1502 retval = ext2fs_open2(device_name, offset_opt, open_flag, 0, 0,
1503 unix_io_manager, &fs);
6304baf2
TT
1504 if (retval) {
1505 com_err (program_name, retval, _("while trying to open %s"),
1506 device_name);
54434927 1507 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
6304baf2
TT
1508 exit(1);
1509 }
1510
92dcfb76 1511skip_device:
8c6b6483 1512 if (strcmp(image_fn, "-") == 0)
1c1e0049
TT
1513 fd = 1;
1514 else {
22b83f62
PS
1515 int o_flags = O_CREAT|O_WRONLY;
1516
1517 if (img_type != E2IMAGE_RAW)
1518 o_flags |= O_TRUNC;
1519 fd = ext2fs_open_file(image_fn, o_flags, 0600);
1c1e0049
TT
1520 if (fd < 0) {
1521 com_err(program_name, errno,
22b83f62 1522 _("while trying to open %s"), image_fn);
1c1e0049
TT
1523 exit(1);
1524 }
6304baf2 1525 }
6b5535f4 1526 if (dest_offset)
80fe25a2 1527 seek_set(fd, dest_offset);
6304baf2 1528
bf0449b1
LC
1529 if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) {
1530 com_err(program_name, 0, "QCOW2 image can not be written to "
1531 "the stdout!\n");
1532 exit(1);
1533 }
0e51f5ae
PS
1534 if (fd != 1) {
1535 if (fstat(fd, &st)) {
1536 com_err(program_name, 0, "Can not stat output\n");
1537 exit(1);
1538 }
1539 if (S_ISBLK(st.st_mode))
1540 output_is_blk = 1;
1541 }
92dcfb76
LC
1542 if (flags & E2IMAGE_IS_QCOW2_FLAG) {
1543 ret = qcow2_write_raw_image(qcow2_fd, fd, header);
1544 if (ret) {
1545 if (ret == -QCOW_COMPRESSED)
1546 fprintf(stderr, "Image (%s) is compressed\n",
1547 image_fn);
1548 if (ret == -QCOW_ENCRYPTED)
1549 fprintf(stderr, "Image (%s) is encrypted\n",
1550 image_fn);
1551 com_err(program_name, ret,
1552 _("while trying to convert qcow2 image"
1553 " (%s) into raw image (%s)"),
1554 device_name, image_fn);
1555 }
1556 goto out;
1557 }
1558
1559
bf0449b1
LC
1560 if (img_type)
1561 write_raw_image_file(fs, fd, img_type, flags);
6304baf2
TT
1562 else
1563 write_image_file(fs, fd);
c5423c5b 1564
72ed1264 1565 ext2fs_close (fs);
92dcfb76
LC
1566out:
1567 if (header)
1568 free(header);
1569 if (qcow2_fd)
1570 close(qcow2_fd);
a6d8302b 1571 remove_error_table(&et_ext2_error_table);
92dcfb76 1572 return ret;
72ed1264 1573}