blob: a924d10a3eb0899e6e40bdf55de4ff64a806f7df [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
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001430int main (int argc, char ** argv)
1431{
1432 int c;
1433 errcode_t retval;
1434 ext2_filsys fs;
JP Abgralle0ed7402014-03-19 19:08:39 -07001435 char *image_fn, offset_opt[64];
1436 struct ext2_qcow2_hdr *header = NULL;
1437 int open_flag = EXT2_FLAG_64BITS;
1438 int img_type = 0;
1439 int flags = 0;
1440 int mount_flags = 0;
1441 int qcow2_fd = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001442 int fd = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -07001443 int ret = 0;
1444 int ignore_rw_mount = 0;
1445 int check = 0;
1446 struct stat st;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001447
1448#ifdef ENABLE_NLS
1449 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -05001450 setlocale(LC_CTYPE, "");
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001451 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1452 textdomain(NLS_CAT_NAME);
JP Abgralle0ed7402014-03-19 19:08:39 -07001453 set_com_err_gettext(gettext);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001454#endif
Theodore Ts'o0f8973f2001-08-27 12:44:23 -04001455 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
1456 E2FSPROGS_DATE);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001457 if (argc && *argv)
1458 program_name = *argv;
Theodore Ts'oa6d83022006-12-26 03:38:07 -05001459 add_error_table(&et_ext2_error_table);
JP Abgralle0ed7402014-03-19 19:08:39 -07001460 while ((c = getopt(argc, argv, "nrsIQafo:O:pc")) != EOF)
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001461 switch (c) {
JP Abgralle0ed7402014-03-19 19:08:39 -07001462 case 'I':
1463 flags |= E2IMAGE_INSTALL_FLAG;
1464 break;
1465 case 'Q':
1466 if (img_type)
1467 usage();
1468 img_type |= E2IMAGE_QCOW2;
1469 break;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001470 case 'r':
JP Abgralle0ed7402014-03-19 19:08:39 -07001471 if (img_type)
1472 usage();
1473 img_type |= E2IMAGE_RAW;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001474 break;
Theodore Ts'od851ed32005-01-19 00:26:43 -05001475 case 's':
JP Abgralle0ed7402014-03-19 19:08:39 -07001476 flags |= E2IMAGE_SCRAMBLE_FLAG;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001477 break;
JP Abgralle0ed7402014-03-19 19:08:39 -07001478 case 'a':
1479 all_data = 1;
1480 break;
1481 case 'f':
1482 ignore_rw_mount = 1;
1483 break;
1484 case 'n':
1485 nop_flag = 1;
1486 break;
1487 case 'o':
1488 source_offset = strtoull(optarg, NULL, 0);
1489 break;
1490 case 'O':
1491 dest_offset = strtoull(optarg, NULL, 0);
1492 break;
1493 case 'p':
1494 show_progress = 1;
1495 break;
1496 case 'c':
1497 check = 1;
Theodore Ts'o38c771d2013-12-25 16:46:39 -05001498 break;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001499 default:
1500 usage();
1501 }
JP Abgralle0ed7402014-03-19 19:08:39 -07001502 if (optind == argc - 1 &&
1503 (source_offset || dest_offset))
1504 move_mode = 1;
1505 else if (optind != argc - 2 )
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001506 usage();
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001507
JP Abgralle0ed7402014-03-19 19:08:39 -07001508 if (all_data && !img_type) {
1509 com_err(program_name, 0, _("-a option can only be used "
1510 "with raw or QCOW2 images."));
1511 exit(1);
1512 }
1513 if ((source_offset || dest_offset) && img_type != E2IMAGE_RAW) {
1514 com_err(program_name, 0,
1515 _("Offsets are only allowed with raw images."));
1516 exit(1);
1517 }
1518 if (move_mode && img_type != E2IMAGE_RAW) {
1519 com_err(program_name, 0,
1520 _("Move mode is only allowed with raw images."));
1521 exit(1);
1522 }
1523 if (move_mode && !all_data) {
1524 com_err(program_name, 0,
1525 _("Move mode requires all data mode."));
1526 exit(1);
1527 }
1528 device_name = argv[optind];
1529 if (move_mode)
1530 image_fn = device_name;
1531 else image_fn = argv[optind+1];
1532
1533 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
1534 if (retval) {
1535 com_err(program_name, retval, _("checking if mounted"));
1536 exit(1);
1537 }
1538
1539 if (img_type && !ignore_rw_mount &&
1540 (mount_flags & EXT2_MF_MOUNTED) &&
1541 !(mount_flags & EXT2_MF_READONLY)) {
1542 fprintf(stderr, _("\nRunning e2image on a R/W mounted "
1543 "filesystem can result in an\n"
1544 "inconsistent image which will not be useful "
1545 "for debugging purposes.\n"
1546 "Use -f option if you really want to do that.\n"));
1547 exit(1);
1548 }
1549
1550 if (flags & E2IMAGE_INSTALL_FLAG) {
1551 install_image(device_name, image_fn, img_type);
Theodore Ts'o6e82cd72005-01-05 03:02:54 -05001552 exit (0);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001553 }
1554
JP Abgralle0ed7402014-03-19 19:08:39 -07001555 if (img_type & E2IMAGE_RAW) {
1556 header = check_qcow2_image(&qcow2_fd, device_name);
1557 if (header) {
1558 flags |= E2IMAGE_IS_QCOW2_FLAG;
1559 goto skip_device;
1560 }
1561 }
1562 sprintf(offset_opt, "offset=%llu", source_offset);
1563 retval = ext2fs_open2(device_name, offset_opt, open_flag, 0, 0,
Theodore Ts'od28759b2013-12-25 14:26:44 -05001564 unix_io_manager, &fs);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001565 if (retval) {
1566 com_err (program_name, retval, _("while trying to open %s"),
1567 device_name);
Theodore Ts'o54434922003-12-07 01:28:50 -05001568 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001569 exit(1);
1570 }
1571
JP Abgralle0ed7402014-03-19 19:08:39 -07001572skip_device:
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001573 if (strcmp(image_fn, "-") == 0)
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001574 fd = 1;
1575 else {
JP Abgralle0ed7402014-03-19 19:08:39 -07001576 int o_flags = O_CREAT|O_RDWR;
1577
1578 if (img_type != E2IMAGE_RAW)
1579 o_flags |= O_TRUNC;
1580 if (access(image_fn, F_OK) != 0)
1581 flags |= E2IMAGE_CHECK_ZERO_FLAG;
1582 fd = ext2fs_open_file(image_fn, o_flags, 0600);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001583 if (fd < 0) {
1584 com_err(program_name, errno,
JP Abgralle0ed7402014-03-19 19:08:39 -07001585 _("while trying to open %s"), image_fn);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001586 exit(1);
1587 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001588 }
JP Abgralle0ed7402014-03-19 19:08:39 -07001589 if (dest_offset)
1590 seek_set(fd, dest_offset);
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001591
JP Abgralle0ed7402014-03-19 19:08:39 -07001592 if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) {
1593 com_err(program_name, 0, _("QCOW2 image can not be written to "
1594 "the stdout!\n"));
1595 exit(1);
1596 }
1597 if (fd != 1) {
1598 if (fstat(fd, &st)) {
1599 com_err(program_name, 0, "Can not stat output\n");
1600 exit(1);
1601 }
1602 if (S_ISBLK(st.st_mode))
1603 output_is_blk = 1;
1604 }
1605 if (flags & E2IMAGE_IS_QCOW2_FLAG) {
1606 ret = qcow2_write_raw_image(qcow2_fd, fd, header);
1607 if (ret) {
1608 if (ret == -QCOW_COMPRESSED)
1609 fprintf(stderr, _("Image (%s) is compressed\n"),
1610 image_fn);
1611 if (ret == -QCOW_ENCRYPTED)
1612 fprintf(stderr, _("Image (%s) is encrypted\n"),
1613 image_fn);
1614 com_err(program_name, ret,
1615 _("while trying to convert qcow2 image"
1616 " (%s) into raw image (%s)"),
1617 device_name, image_fn);
1618 }
1619 goto out;
1620 }
1621
1622 if (check) {
1623 if (img_type != E2IMAGE_RAW) {
1624 fprintf(stderr, _("The -c option only supported "
1625 "in raw mode\n"));
1626 exit(1);
1627 }
1628 if (fd == 1) {
1629 fprintf(stderr, _("The -c option is not supported "
1630 "when writing to stdout\n"));
1631 exit(1);
1632 }
1633 retval = ext2fs_get_mem(fs->blocksize, &check_buf);
1634 if (retval) {
1635 com_err(program_name, retval,
1636 _("while allocating check_buf"));
1637 exit(1);
1638 }
1639 }
1640 if (show_progress && (img_type != E2IMAGE_RAW)) {
1641 fprintf(stderr, _("The -p option only supported "
1642 "in raw mode\n"));
1643 exit(1);
1644 }
1645 if (img_type)
1646 write_raw_image_file(fs, fd, img_type, flags);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001647 else
1648 write_image_file(fs, fd);
Theodore Ts'oc5423c52001-02-08 05:45:17 +00001649
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001650 ext2fs_close (fs);
JP Abgralle0ed7402014-03-19 19:08:39 -07001651 if (check)
1652 printf(_("%d blocks already contained the data to be copied.\n"),
1653 skipped_blocks);
1654
1655out:
1656 if (header)
1657 free(header);
1658 if (qcow2_fd)
1659 close(qcow2_fd);
Theodore Ts'oa6d83022006-12-26 03:38:07 -05001660 remove_error_table(&et_ext2_error_table);
JP Abgralle0ed7402014-03-19 19:08:39 -07001661 return ret;
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001662}