blob: 3a956efb8475671cfb1cc9c9fe67c2d2e38f42e1 [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'od1154eb2011-09-18 17:34:37 -040016#include "config.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000017#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>
Theodore Ts'of38cf3c2008-09-01 11:17:29 -040027#ifdef HAVE_STDLIB_H
Theodore Ts'o72ed1262000-11-12 19:32:20 +000028#include <stdlib.h>
Theodore Ts'of38cf3c2008-09-01 11:17:29 -040029#endif
Theodore Ts'o72ed1262000-11-12 19:32:20 +000030#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>
Lukas Czernerbf0449b2011-05-18 13:36:53 +020037#include <assert.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"
Lukas Czernerbf0449b2011-05-18 13:36:53 +020045#include "ext2fs/qcow2.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000046
47#include "../version.h"
48#include "nls-enable.h"
49
Lukas Czernerbf0449b2011-05-18 13:36:53 +020050#define QCOW_OFLAG_COPIED (1LL << 63)
51
52
Theodore Ts'o3e377db2000-12-09 02:37:33 +000053const char * program_name = "e2image";
Theodore Ts'o72ed1262000-11-12 19:32:20 +000054char * device_name = NULL;
55
Lukas Czernerbf0449b2011-05-18 13:36:53 +020056static void lseek_error_and_exit(int errnum)
57{
Theodore Ts'oe64e6762012-04-05 12:13:05 -070058 fprintf(stderr, "seek: %s\n", error_message(errnum));
Lukas Czernerbf0449b2011-05-18 13:36:53 +020059 exit(1);
60}
61
62static blk64_t align_offset(blk64_t offset, int n)
63{
64 return (offset + n - 1) & ~(n - 1);
65}
66
67static int get_bits_from_size(size_t size)
68{
69 int res = 0;
70
71 if (size == 0)
72 return -1;
73
74 while (size != 1) {
75 /* Not a power of two */
76 if (size & 1)
77 return -1;
78
79 size >>= 1;
80 res++;
81 }
82 return res;
83}
84
Theodore Ts'o72ed1262000-11-12 19:32:20 +000085static void usage(void)
86{
Lukas Czernerbf0449b2011-05-18 13:36:53 +020087 fprintf(stderr, _("Usage: %s [-rsIQ] device image_file\n"),
Theodore Ts'od851ed32005-01-19 00:26:43 -050088 program_name);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000089 exit (1);
90}
91
Lukas Czernerbf0449b2011-05-18 13:36:53 +020092static void generic_write(int fd, void *buf, int blocksize, blk64_t block)
93{
94 int count, free_buf = 0;
95 errcode_t err;
Lukas Czernerbf0449b2011-05-18 13:36:53 +020096
97 if (!blocksize)
98 return;
99
100 if (!buf) {
101 free_buf = 1;
102 err = ext2fs_get_arrayzero(1, blocksize, &buf);
103 if (err) {
104 com_err(program_name, err, "while allocating buffer");
105 exit(1);
106 }
107 }
108
109 count = write(fd, buf, blocksize);
110 if (count != blocksize) {
111 if (count == -1)
112 err = errno;
113 else
114 err = 0;
115
116 if (block)
117 com_err(program_name, err, "error writing block %llu",
118 block);
119 else
120 com_err(program_name, err, "error in write()");
121
122 exit(1);
123 }
124 if (free_buf)
125 ext2fs_free_mem(&buf);
126}
127
128static void write_header(int fd, void *hdr, int hdr_size, int wrt_size)
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000129{
Theodore Ts'o095b4592001-05-03 13:33:11 +0000130 char *header_buf;
Andreas Dilger96367ad2011-06-15 22:17:38 -0400131 int ret;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000132
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200133 /* Sanity check */
134 if (hdr_size > wrt_size) {
135 fprintf(stderr, _("Error: header size is bigger than "
136 "wrt_size\n"));
137 }
138
139 ret = ext2fs_get_mem(wrt_size, &header_buf);
140 if (ret) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500141 fputs(_("Couldn't allocate header buffer\n"), stderr);
Theodore Ts'o095b4592001-05-03 13:33:11 +0000142 exit(1);
143 }
144
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200145 if (ext2fs_llseek(fd, 0, SEEK_SET) < 0) {
146 perror("ext2fs_llseek while writing header");
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000147 exit(1);
148 }
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200149 memset(header_buf, 0, wrt_size);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400150
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000151 if (hdr)
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200152 memcpy(header_buf, hdr, hdr_size);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400153
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200154 generic_write(fd, header_buf, wrt_size, 0);
155
156 ext2fs_free_mem(&header_buf);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000157}
158
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400159static void write_image_file(ext2_filsys fs, int fd)
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000160{
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400161 struct ext2_image_hdr hdr;
162 struct stat st;
163 errcode_t retval;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000164
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200165 write_header(fd, NULL, fs->blocksize, fs->blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000166 memset(&hdr, 0, sizeof(struct ext2_image_hdr));
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400167
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200168 hdr.offset_super = ext2fs_llseek(fd, 0, SEEK_CUR);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000169 retval = ext2fs_image_super_write(fs, fd, 0);
170 if (retval) {
171 com_err(program_name, retval, _("while writing superblock"));
172 exit(1);
173 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400174
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200175 hdr.offset_inode = ext2fs_llseek(fd, 0, SEEK_CUR);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400176 retval = ext2fs_image_inode_write(fs, fd,
177 (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000178 if (retval) {
179 com_err(program_name, retval, _("while writing inode table"));
180 exit(1);
181 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400182
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200183 hdr.offset_blockmap = ext2fs_llseek(fd, 0, SEEK_CUR);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000184 retval = ext2fs_image_bitmap_write(fs, fd, 0);
185 if (retval) {
186 com_err(program_name, retval, _("while writing block bitmap"));
187 exit(1);
188 }
189
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200190 hdr.offset_inodemap = ext2fs_llseek(fd, 0, SEEK_CUR);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000191 retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
192 if (retval) {
193 com_err(program_name, retval, _("while writing inode bitmap"));
194 exit(1);
195 }
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000196
197 hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
198 strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
199 gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
Theodore Ts'odf200ff2008-01-27 15:45:30 -0500200 strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1);
Theodore Ts'o095b4592001-05-03 13:33:11 +0000201 hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
202 hdr.fs_blocksize = fs->blocksize;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400203
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000204 if (stat(device_name, &st) == 0)
205 hdr.fs_device = st.st_rdev;
206
207 if (fstat(fd, &st) == 0) {
208 hdr.image_device = st.st_dev;
209 hdr.image_inode = st.st_ino;
210 }
211 memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
212
213 hdr.image_time = time(0);
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200214 write_header(fd, &hdr, fs->blocksize, fs->blocksize);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400215}
216
217/*
218 * These set of functions are used to write a RAW image file.
219 */
220ext2fs_block_bitmap meta_block_map;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500221ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200222blk64_t meta_blocks_count;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400223
224struct process_block_struct {
225 ext2_ino_t ino;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500226 int is_dir;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400227};
228
229/*
230 * These subroutines short circuits ext2fs_get_blocks and
231 * ext2fs_check_directory; we use them since we already have the inode
232 * structure, so there's no point in letting the ext2fs library read
233 * the inode again.
234 */
235static ino_t stashed_ino = 0;
236static struct ext2_inode *stashed_inode;
237
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400238static errcode_t meta_get_blocks(ext2_filsys fs EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500239 ext2_ino_t ino,
240 blk_t *blocks)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400241{
242 int i;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400243
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400244 if ((ino != stashed_ino) || !stashed_inode)
245 return EXT2_ET_CALLBACK_NOTHANDLED;
246
247 for (i=0; i < EXT2_N_BLOCKS; i++)
248 blocks[i] = stashed_inode->i_block[i];
249 return 0;
250}
251
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400252static errcode_t meta_check_directory(ext2_filsys fs EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500253 ext2_ino_t ino)
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500254{
255 if ((ino != stashed_ino) || !stashed_inode)
256 return EXT2_ET_CALLBACK_NOTHANDLED;
257
258 if (!LINUX_S_ISDIR(stashed_inode->i_mode))
259 return EXT2_ET_NO_DIRECTORY;
260 return 0;
261}
262
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400263static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500264 ext2_ino_t ino,
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400265 struct ext2_inode *inode)
266{
267 if ((ino != stashed_ino) || !stashed_inode)
268 return EXT2_ET_CALLBACK_NOTHANDLED;
269 *inode = *stashed_inode;
270 return 0;
271}
272
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500273static void use_inode_shortcuts(ext2_filsys fs, int bool)
274{
275 if (bool) {
276 fs->get_blocks = meta_get_blocks;
277 fs->check_directory = meta_check_directory;
278 fs->read_inode = meta_read_inode;
279 stashed_ino = 0;
280 } else {
281 fs->get_blocks = 0;
282 fs->check_directory = 0;
283 fs->read_inode = 0;
284 }
285}
286
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400287static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400288 blk64_t *block_nr,
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400289 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400290 blk64_t ref_block EXT2FS_ATTR((unused)),
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400291 int ref_offset EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500292 void *priv_data EXT2FS_ATTR((unused)))
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400293{
Theodore Ts'od851ed32005-01-19 00:26:43 -0500294 struct process_block_struct *p;
295
296 p = (struct process_block_struct *) priv_data;
297
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400298 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200299 meta_blocks_count++;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400300 if (scramble_block_map && p->is_dir && blockcnt >= 0)
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400301 ext2fs_mark_block_bitmap2(scramble_block_map, *block_nr);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400302 return 0;
303}
304
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400305static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)),
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400306 blk64_t *block_nr,
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400307 e2_blkcnt_t blockcnt,
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400308 blk64_t ref_block EXT2FS_ATTR((unused)),
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400309 int ref_offset EXT2FS_ATTR((unused)),
Theodore Ts'o54434922003-12-07 01:28:50 -0500310 void *priv_data EXT2FS_ATTR((unused)))
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400311{
312 if (blockcnt < 0) {
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400313 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200314 meta_blocks_count++;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400315 }
316 return 0;
317}
318
319static void mark_table_blocks(ext2_filsys fs)
320{
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400321 blk64_t first_block, b;
Theodore Ts'o54434922003-12-07 01:28:50 -0500322 unsigned int i,j;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400323
Eric Sandeenbb1a46a2006-09-12 14:55:22 -0400324 first_block = fs->super->s_first_data_block;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400325 /*
326 * Mark primary superblock
327 */
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400328 ext2fs_mark_block_bitmap2(meta_block_map, first_block);
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200329 meta_blocks_count++;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400330
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400331 /*
332 * Mark the primary superblock descriptors
333 */
334 for (j = 0; j < fs->desc_blocks; j++) {
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400335 ext2fs_mark_block_bitmap2(meta_block_map,
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400336 ext2fs_descriptor_block_loc2(fs, first_block, j));
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400337 }
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200338 meta_blocks_count += fs->desc_blocks;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400339
340 for (i = 0; i < fs->group_desc_count; i++) {
341 /*
342 * Mark the blocks used for the inode table
343 */
Phillip Susi6c9ce8a2012-02-27 00:56:49 -0500344 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) &&
345 ext2fs_inode_table_loc(fs, i)) {
346 unsigned int end = (unsigned) fs->inode_blocks_per_group;
347 /* skip unused blocks */
348 if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
349 EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
350 end -= (ext2fs_bg_itable_unused(fs, i) /
351 EXT2_INODES_PER_BLOCK(fs->super));
Valerie Aurora Hensond7cca6b2009-10-25 21:43:47 -0400352 for (j = 0, b = ext2fs_inode_table_loc(fs, i);
Phillip Susi6c9ce8a2012-02-27 00:56:49 -0500353 j < end;
354 j++, b++) {
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400355 ext2fs_mark_block_bitmap2(meta_block_map, b);
Phillip Susi6c9ce8a2012-02-27 00:56:49 -0500356 meta_blocks_count++;
357 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400358 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400359
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400360 /*
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400361 * Mark block used for the block bitmap
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400362 */
Phillip Susi6c9ce8a2012-02-27 00:56:49 -0500363 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) &&
364 ext2fs_block_bitmap_loc(fs, i)) {
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400365 ext2fs_mark_block_bitmap2(meta_block_map,
Valerie Aurora Hensond7cca6b2009-10-25 21:43:47 -0400366 ext2fs_block_bitmap_loc(fs, i));
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200367 meta_blocks_count++;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400368 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400369
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400370 /*
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400371 * Mark block used for the inode bitmap
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400372 */
Phillip Susi6c9ce8a2012-02-27 00:56:49 -0500373 if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) &&
374 ext2fs_inode_bitmap_loc(fs, i)) {
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400375 ext2fs_mark_block_bitmap2(meta_block_map,
Valerie Aurora Hensond7cca6b2009-10-25 21:43:47 -0400376 ext2fs_inode_bitmap_loc(fs, i));
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200377 meta_blocks_count++;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400378 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400379 }
380}
381
382/*
383 * This function returns 1 if the specified block is all zeros
384 */
385static int check_zero_block(char *buf, int blocksize)
386{
387 char *cp = buf;
388 int left = blocksize;
389
390 while (left > 0) {
391 if (*cp++)
392 return 0;
393 left--;
394 }
395 return 1;
396}
397
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500398static void write_block(int fd, char *buf, int sparse_offset,
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400399 int blocksize, blk64_t block)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400400{
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200401 ext2_loff_t ret = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400402
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200403 if (sparse_offset)
404 ret = ext2fs_llseek(fd, sparse_offset, SEEK_CUR);
405
406 if (ret < 0)
407 lseek_error_and_exit(errno);
408 generic_write(fd, buf, blocksize, block);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400409}
410
Theodore Ts'od851ed32005-01-19 00:26:43 -0500411int name_id[256];
412
Theodore Ts'o8a480352009-06-21 21:07:38 -0400413#define EXT4_MAX_REC_LEN ((1<<16)-1)
414
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400415static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf)
Theodore Ts'od851ed32005-01-19 00:26:43 -0500416{
417 char *p, *end, *cp;
418 struct ext2_dir_entry_2 *dirent;
Theodore Ts'o8a480352009-06-21 21:07:38 -0400419 unsigned int rec_len;
420 int id, len;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500421
Theodore Ts'od851ed32005-01-19 00:26:43 -0500422 end = buf + fs->blocksize;
423 for (p = buf; p < end-8; p += rec_len) {
424 dirent = (struct ext2_dir_entry_2 *) p;
425 rec_len = dirent->rec_len;
Theodore Ts'o2eae0932007-08-11 02:57:31 -0400426#ifdef WORDS_BIGENDIAN
427 rec_len = ext2fs_swab16(rec_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500428#endif
Theodore Ts'o8a480352009-06-21 21:07:38 -0400429 if (rec_len == EXT4_MAX_REC_LEN || rec_len == 0)
430 rec_len = fs->blocksize;
431 else
432 rec_len = (rec_len & 65532) | ((rec_len & 3) << 16);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500433#if 0
434 printf("rec_len = %d, name_len = %d\n", rec_len, dirent->name_len);
435#endif
436 if (rec_len < 8 || (rec_len % 4) ||
437 (p+rec_len > end)) {
438 printf("Corrupt directory block %lu: "
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400439 "bad rec_len (%d)\n", (unsigned long) blk,
Theodore Ts'o9b9a7802005-12-10 21:50:30 -0500440 rec_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500441 rec_len = end - p;
Theodore Ts'o8a480352009-06-21 21:07:38 -0400442 (void) ext2fs_set_rec_len(fs, rec_len,
443 (struct ext2_dir_entry *) dirent);
Theodore Ts'o2eae0932007-08-11 02:57:31 -0400444#ifdef WORDS_BIGENDIAN
Theodore Ts'o8a480352009-06-21 21:07:38 -0400445 dirent->rec_len = ext2fs_swab16(dirent->rec_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500446#endif
447 continue;
448 }
449 if (dirent->name_len + 8 > rec_len) {
450 printf("Corrupt directory block %lu: "
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400451 "bad name_len (%d)\n", (unsigned long) blk,
Theodore Ts'o9b9a7802005-12-10 21:50:30 -0500452 dirent->name_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500453 dirent->name_len = rec_len - 8;
454 continue;
455 }
Theodore Ts'od851ed32005-01-19 00:26:43 -0500456 cp = p+8;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500457 len = rec_len - dirent->name_len - 8;
458 if (len > 0)
459 memset(cp+dirent->name_len, 0, len);
Theodore Ts'o79fc2a92005-01-26 11:37:46 -0500460 if (dirent->name_len==1 && cp[0] == '.')
461 continue;
462 if (dirent->name_len==2 && cp[0] == '.' && cp[1] == '.')
463 continue;
464
465 memset(cp, 'A', dirent->name_len);
Theodore Ts'od851ed32005-01-19 00:26:43 -0500466 len = dirent->name_len;
467 id = name_id[len]++;
468 while ((len > 0) && (id > 0)) {
469 *cp += id % 26;
470 id = id / 26;
471 cp++;
472 len--;
473 }
474 }
475}
476
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500477static void output_meta_data_blocks(ext2_filsys fs, int fd)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400478{
479 errcode_t retval;
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400480 blk64_t blk;
Theodore Ts'od851ed32005-01-19 00:26:43 -0500481 char *buf, *zero_buf;
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500482 int sparse = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400483
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200484 retval = ext2fs_get_mem(fs->blocksize, &buf);
485 if (retval) {
486 com_err(program_name, retval, "while allocating buffer");
Theodore Ts'od851ed32005-01-19 00:26:43 -0500487 exit(1);
488 }
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200489 retval = ext2fs_get_memzero(fs->blocksize, &zero_buf);
490 if (retval) {
491 com_err(program_name, retval, "while allocating buffer");
Theodore Ts'od851ed32005-01-19 00:26:43 -0500492 exit(1);
493 }
Valerie Aurora Henson4efbac62009-09-07 20:46:34 -0400494 for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) {
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400495 if ((blk >= fs->super->s_first_data_block) &&
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400496 ext2fs_test_block_bitmap2(meta_block_map, blk)) {
Valerie Aurora Henson24a117a2009-09-07 21:14:24 -0400497 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400498 if (retval) {
499 com_err(program_name, retval,
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -0400500 "error reading block %llu", blk);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400501 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400502 if (scramble_block_map &&
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -0400503 ext2fs_test_block_bitmap2(scramble_block_map, blk))
Theodore Ts'od851ed32005-01-19 00:26:43 -0500504 scramble_dir_block(fs, blk, buf);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400505 if ((fd != 1) && check_zero_block(buf, fs->blocksize))
506 goto sparse_write;
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500507 write_block(fd, buf, sparse, fs->blocksize, blk);
508 sparse = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400509 } else {
510 sparse_write:
511 if (fd == 1) {
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500512 write_block(fd, zero_buf, 0,
513 fs->blocksize, blk);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400514 continue;
515 }
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500516 sparse += fs->blocksize;
Theodore Ts'o2b7a30c2012-02-17 09:19:33 -0500517 if (sparse > 1024*1024) {
518 write_block(fd, 0, 1024*1024, 0, 0);
519 sparse -= 1024*1024;
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500520 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400521 }
522 }
Theodore Ts'o50a676e2012-02-17 09:23:12 -0500523#ifdef HAVE_FTRUNCATE64
524 if (sparse) {
525 ext2_loff_t offset = ext2fs_llseek(fd, sparse, SEEK_CUR);
526
527 if (offset < 0)
528 lseek_error_and_exit(errno);
529 if (ftruncate64(fd, offset) < 0)
530 write_block(fd, zero_buf, -1, 1, -1);
531 }
532#else
Arun Thomas20abd3e2007-11-09 15:46:10 -0500533 if (sparse)
534 write_block(fd, zero_buf, sparse-1, 1, -1);
Theodore Ts'o50a676e2012-02-17 09:23:12 -0500535#endif
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200536 ext2fs_free_mem(&zero_buf);
537 ext2fs_free_mem(&buf);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400538}
539
Theodore Ts'oe64e6762012-04-05 12:13:05 -0700540static void init_l1_table(struct ext2_qcow2_image *image)
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200541{
542 __u64 *l1_table;
543 errcode_t ret;
544
545 ret = ext2fs_get_arrayzero(image->l1_size, sizeof(__u64), &l1_table);
546 if (ret) {
547 com_err(program_name, ret, "while allocating l1 table");
548 exit(1);
549 }
550
551 image->l1_table = l1_table;
552}
553
554static void init_l2_cache(struct ext2_qcow2_image *image)
555{
556 unsigned int count, i;
557 struct ext2_qcow2_l2_cache *cache;
558 struct ext2_qcow2_l2_table *table;
559 errcode_t ret;
560
561 ret = ext2fs_get_arrayzero(1, sizeof(struct ext2_qcow2_l2_cache),
562 &cache);
563 if (ret)
564 goto alloc_err;
565
566 count = (image->l1_size > L2_CACHE_PREALLOC) ? L2_CACHE_PREALLOC :
567 image->l1_size;
568
569 cache->count = count;
570 cache->free = count;
571 cache->next_offset = image->l2_offset;
572
573 for (i = 0; i < count; i++) {
574 ret = ext2fs_get_arrayzero(1,
575 sizeof(struct ext2_qcow2_l2_table), &table);
576 if (ret)
577 goto alloc_err;
578
579 ret = ext2fs_get_arrayzero(image->l2_size,
580 sizeof(__u64), &table->data);
581 if (ret)
582 goto alloc_err;
583
584 table->next = cache->free_head;
585 cache->free_head = table;
586 }
587
588 image->l2_cache = cache;
589 return;
590
591alloc_err:
592 com_err(program_name, ret, "while allocating l2 cache");
593 exit(1);
594}
595
596static void put_l2_cache(struct ext2_qcow2_image *image)
597{
598 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
599 struct ext2_qcow2_l2_table *tmp, *table;
600
601 if (!cache)
602 return;
603
604 table = cache->free_head;
605 cache->free_head = NULL;
606again:
607 while (table) {
608 tmp = table;
609 table = table->next;
610 ext2fs_free_mem(&tmp->data);
611 ext2fs_free_mem(&tmp);
612 }
613
614 if (cache->free != cache->count) {
615 fprintf(stderr, "Warning: There are still tables in the "
616 "cache while putting the cache, data will "
617 "be lost so the image may not be valid.\n");
618 table = cache->used_head;
619 cache->used_head = NULL;
620 goto again;
621 }
622
623 ext2fs_free_mem(&cache);
624}
625
626static int init_refcount(struct ext2_qcow2_image *img, blk64_t table_offset)
627{
628 struct ext2_qcow2_refcount *ref;
629 blk64_t table_clusters;
630 errcode_t ret;
631
632 ref = &(img->refcount);
633
634 /*
635 * One refcount block addresses 2048 clusters, one refcount table
636 * addresses cluster/sizeof(__u64) refcount blocks, and we need
637 * to address meta_blocks_count clusters + qcow2 metadata clusters
638 * in the worst case.
639 */
640 table_clusters = meta_blocks_count + (table_offset >>
641 img->cluster_bits);
642 table_clusters >>= (img->cluster_bits + 6 - 1);
643 table_clusters = (table_clusters == 0) ? 1 : table_clusters;
644
645 ref->refcount_table_offset = table_offset;
646 ref->refcount_table_clusters = table_clusters;
647 ref->refcount_table_index = 0;
648 ref->refcount_block_index = 0;
649
650 /* Allocate refcount table */
651 ret = ext2fs_get_arrayzero(ref->refcount_table_clusters,
652 img->cluster_size, &ref->refcount_table);
653 if (ret)
654 return ret;
655
656 /* Allocate refcount block */
657 ret = ext2fs_get_arrayzero(1, img->cluster_size, &ref->refcount_block);
658 if (ret)
659 ext2fs_free_mem(&ref->refcount_table);
660
661 return ret;
662}
663
664static int initialize_qcow2_image(int fd, ext2_filsys fs,
665 struct ext2_qcow2_image *image)
666{
667 struct ext2_qcow2_hdr *header;
668 blk64_t total_size, offset;
669 int shift, l2_bits, header_size, l1_size, ret;
670 int cluster_bits = get_bits_from_size(fs->blocksize);
671 struct ext2_super_block *sb = fs->super;
672
673 /* Allocate header */
674 ret = ext2fs_get_memzero(sizeof(struct ext2_qcow2_hdr), &header);
675 if (ret)
676 return ret;
677
678 total_size = ext2fs_blocks_count(sb) << cluster_bits;
679 image->cluster_size = fs->blocksize;
680 image->l2_size = 1 << (cluster_bits - 3);
681 image->cluster_bits = cluster_bits;
682 image->fd = fd;
683
684 header->magic = ext2fs_cpu_to_be32(QCOW_MAGIC);
685 header->version = ext2fs_cpu_to_be32(QCOW_VERSION);
686 header->size = ext2fs_cpu_to_be64(total_size);
687 header->cluster_bits = ext2fs_cpu_to_be32(cluster_bits);
688
689 header_size = (sizeof(struct ext2_qcow2_hdr) + 7) & ~7;
690 offset = align_offset(header_size, image->cluster_size);
691
692 header->l1_table_offset = ext2fs_cpu_to_be64(offset);
693 image->l1_offset = offset;
694
695 l2_bits = cluster_bits - 3;
696 shift = cluster_bits + l2_bits;
697 l1_size = ((total_size + (1LL << shift) - 1) >> shift);
698 header->l1_size = ext2fs_cpu_to_be32(l1_size);
699 image->l1_size = l1_size;
700
701 /* Make space for L1 table */
702 offset += align_offset(l1_size * sizeof(blk64_t), image->cluster_size);
703
704 /* Initialize refcounting */
705 ret = init_refcount(image, offset);
706 if (ret) {
707 ext2fs_free_mem(&header);
708 return ret;
709 }
710 header->refcount_table_offset = ext2fs_cpu_to_be64(offset);
711 header->refcount_table_clusters =
712 ext2fs_cpu_to_be32(image->refcount.refcount_table_clusters);
713 offset += image->cluster_size;
714 offset += image->refcount.refcount_table_clusters <<
715 image->cluster_bits;
716
717 /* Make space for L2 tables */
718 image->l2_offset = offset;
719 offset += image->cluster_size;
720
721 /* Make space for first refcount block */
722 image->refcount.refcount_block_offset = offset;
723
724 image->hdr = header;
725 /* Initialize l1 and l2 tables */
Theodore Ts'oe64e6762012-04-05 12:13:05 -0700726 init_l1_table(image);
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200727 init_l2_cache(image);
728
729 return 0;
730}
731
732static void free_qcow2_image(struct ext2_qcow2_image *img)
733{
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200734 if (!img)
735 return;
736
737 if (img->hdr)
738 ext2fs_free_mem(&img->hdr);
739
740 if (img->l1_table)
741 ext2fs_free_mem(&img->l1_table);
742
743 if (img->refcount.refcount_table)
744 ext2fs_free_mem(&img->refcount.refcount_table);
745 if (img->refcount.refcount_block)
746 ext2fs_free_mem(&img->refcount.refcount_block);
747
748 put_l2_cache(img);
749
750 ext2fs_free_mem(&img);
751}
752
753/**
754 * Put table from used list (used_head) into free list (free_head).
755 * l2_table is used to return pointer to the next used table (used_head).
756 */
757static void put_used_table(struct ext2_qcow2_image *img,
758 struct ext2_qcow2_l2_table **l2_table)
759{
760 struct ext2_qcow2_l2_cache *cache = img->l2_cache;
761 struct ext2_qcow2_l2_table *table;
762
763 table = cache->used_head;
764 cache->used_head = table->next;
765
766 assert(table);
767 if (!table->next)
768 cache->used_tail = NULL;
769
770 /* Clean the table for case we will need to use it again */
771 memset(table->data, 0, img->cluster_size);
772 table->next = cache->free_head;
773 cache->free_head = table;
774
775 cache->free++;
776
777 *l2_table = cache->used_head;
778}
779
780static void flush_l2_cache(struct ext2_qcow2_image *image)
781{
Lukas Czerner9d10f012011-09-14 13:27:44 -0400782 blk64_t seek = 0;
783 ext2_loff_t offset;
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200784 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
785 struct ext2_qcow2_l2_table *table = cache->used_head;
786 int fd = image->fd;
787
788 /* Store current position */
789 if ((offset = ext2fs_llseek(fd, 0, SEEK_CUR)) < 0)
790 lseek_error_and_exit(errno);
791
792 assert(table);
793 while (cache->free < cache->count) {
794 if (seek != table->offset) {
795 if (ext2fs_llseek(fd, table->offset, SEEK_SET) < 0)
796 lseek_error_and_exit(errno);
797 seek = table->offset;
798 }
799
800 generic_write(fd, (char *)table->data, image->cluster_size , 0);
801 put_used_table(image, &table);
802 seek += image->cluster_size;
803 }
804
805 /* Restore previous position */
806 if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
807 lseek_error_and_exit(errno);
808}
809
810/**
811 * Get first free table (from free_head) and put it into tail of used list
812 * (to used_tail).
813 * l2_table is used to return pointer to moved table.
814 * Returns 1 if the cache is full, 0 otherwise.
815 */
816static void get_free_table(struct ext2_qcow2_image *image,
817 struct ext2_qcow2_l2_table **l2_table)
818{
819 struct ext2_qcow2_l2_table *table;
820 struct ext2_qcow2_l2_cache *cache = image->l2_cache;
821
822 if (0 == cache->free)
823 flush_l2_cache(image);
824
825 table = cache->free_head;
826 assert(table);
827 cache->free_head = table->next;
828
829 if (cache->used_tail)
830 cache->used_tail->next = table;
831 else
832 /* First item in the used list */
833 cache->used_head = table;
834
835 cache->used_tail = table;
836 cache->free--;
837
838 *l2_table = table;
839}
840
841static int add_l2_item(struct ext2_qcow2_image *img, blk64_t blk,
842 blk64_t data, blk64_t next)
843{
844 struct ext2_qcow2_l2_cache *cache = img->l2_cache;
845 struct ext2_qcow2_l2_table *table = cache->used_tail;
846 blk64_t l1_index = blk / img->l2_size;
847 blk64_t l2_index = blk & (img->l2_size - 1);
848 int ret = 0;
849
850 /*
851 * Need to create new table if it does not exist,
852 * or if it is full
853 */
854 if (!table || (table->l1_index != l1_index)) {
855 get_free_table(img, &table);
856 table->l1_index = l1_index;
857 table->offset = cache->next_offset;
858 cache->next_offset = next;
859 img->l1_table[l1_index] =
860 ext2fs_cpu_to_be64(table->offset | QCOW_OFLAG_COPIED);
861 ret++;
862 }
863
864 table->data[l2_index] = ext2fs_cpu_to_be64(data | QCOW_OFLAG_COPIED);
865 return ret;
866}
867
868static int update_refcount(int fd, struct ext2_qcow2_image *img,
869 blk64_t offset, blk64_t rfblk_pos)
870{
871 struct ext2_qcow2_refcount *ref;
872 __u32 table_index;
873 int ret = 0;
874
875 ref = &(img->refcount);
876 table_index = offset >> (2 * img->cluster_bits - 1);
877
878 /*
879 * Need to create new refcount block when the offset addresses
880 * another item in the refcount table
881 */
882 if (table_index != ref->refcount_table_index) {
883
884 if (ext2fs_llseek(fd, ref->refcount_block_offset, SEEK_SET) < 0)
885 lseek_error_and_exit(errno);
886
887 generic_write(fd, (char *)ref->refcount_block,
888 img->cluster_size, 0);
889 memset(ref->refcount_block, 0, img->cluster_size);
890
891 ref->refcount_table[ref->refcount_table_index] =
892 ext2fs_cpu_to_be64(ref->refcount_block_offset);
893 ref->refcount_block_offset = rfblk_pos;
894 ref->refcount_block_index = 0;
895 ref->refcount_table_index = table_index;
896 ret++;
897 }
898
899 /*
900 * We are relying on the fact that we are creating the qcow2
901 * image sequentially, hence we will always allocate refcount
902 * block items sequentialy.
903 */
904 ref->refcount_block[ref->refcount_block_index] = ext2fs_cpu_to_be16(1);
905 ref->refcount_block_index++;
906 return ret;
907}
908
909static int sync_refcount(int fd, struct ext2_qcow2_image *img)
910{
911 struct ext2_qcow2_refcount *ref;
912
913 ref = &(img->refcount);
914
915 ref->refcount_table[ref->refcount_table_index] =
916 ext2fs_cpu_to_be64(ref->refcount_block_offset);
917 if (ext2fs_llseek(fd, ref->refcount_table_offset, SEEK_SET) < 0)
918 lseek_error_and_exit(errno);
919 generic_write(fd, (char *)ref->refcount_table,
920 ref->refcount_table_clusters << img->cluster_bits, 0);
921
922 if (ext2fs_llseek(fd, ref->refcount_block_offset, SEEK_SET) < 0)
923 lseek_error_and_exit(errno);
924 generic_write(fd, (char *)ref->refcount_block, img->cluster_size, 0);
925 return 0;
926}
927
928static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd)
929{
930 errcode_t retval;
Andreas Dilger96367ad2011-06-15 22:17:38 -0400931 blk64_t blk, offset, size, end;
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200932 char *buf;
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200933 struct ext2_qcow2_image *img;
Andreas Dilger96367ad2011-06-15 22:17:38 -0400934 unsigned int header_size;
Lukas Czernerbf0449b2011-05-18 13:36:53 +0200935
936 /* allocate struct ext2_qcow2_image */
937 retval = ext2fs_get_mem(sizeof(struct ext2_qcow2_image), &img);
938 if (retval) {
939 com_err(program_name, retval,
940 "while allocating ext2_qcow2_image");
941 exit(1);
942 }
943
944 retval = initialize_qcow2_image(fd, fs, img);
945 if (retval) {
946 com_err(program_name, retval,
947 "while initializing ext2_qcow2_image");
948 exit(1);
949 }
950 header_size = align_offset(sizeof(struct ext2_qcow2_hdr),
951 img->cluster_size);
952 write_header(fd, img->hdr, sizeof(struct ext2_qcow2_hdr), header_size);
953
954 /* Refcount all qcow2 related metadata up to refcount_block_offset */
955 end = img->refcount.refcount_block_offset;
956 if (ext2fs_llseek(fd, end, SEEK_SET) < 0)
957 lseek_error_and_exit(errno);
958 blk = end + img->cluster_size;
959 for (offset = 0; offset <= end; offset += img->cluster_size) {
960 if (update_refcount(fd, img, offset, blk)) {
961 blk += img->cluster_size;
962 /*
963 * If we create new refcount block, we need to refcount
964 * it as well.
965 */
966 end += img->cluster_size;
967 }
968 }
969 if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
970 lseek_error_and_exit(errno);
971
972 retval = ext2fs_get_mem(fs->blocksize, &buf);
973 if (retval) {
974 com_err(program_name, retval, "while allocating buffer");
975 exit(1);
976 }
977 /* Write qcow2 data blocks */
978 for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) {
979 if ((blk >= fs->super->s_first_data_block) &&
980 ext2fs_test_block_bitmap2(meta_block_map, blk)) {
981 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
982 if (retval) {
983 com_err(program_name, retval,
984 "error reading block %llu", blk);
985 continue;
986 }
987 if (scramble_block_map &&
988 ext2fs_test_block_bitmap2(scramble_block_map, blk))
989 scramble_dir_block(fs, blk, buf);
990 if (check_zero_block(buf, fs->blocksize))
991 continue;
992
993 if (update_refcount(fd, img, offset, offset)) {
994 /* Make space for another refcount block */
995 offset += img->cluster_size;
996 if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
997 lseek_error_and_exit(errno);
998 /*
999 * We have created the new refcount block, this
1000 * means that we need to refcount it as well.
1001 * So the previous update_refcount refcounted
1002 * the block itself and now we are going to
1003 * create refcount for data. New refcount
1004 * block should not be created!
1005 */
1006 if (update_refcount(fd, img, offset, offset)) {
1007 fprintf(stderr, "Programming error: "
1008 "multiple sequential refcount "
1009 "blocks created!\n");
1010 exit(1);
1011 }
1012 }
1013
1014 generic_write(fd, buf, fs->blocksize, 0);
1015
1016 if (add_l2_item(img, blk, offset,
1017 offset + img->cluster_size)) {
1018 offset += img->cluster_size;
1019 if (update_refcount(fd, img, offset,
1020 offset + img->cluster_size)) {
1021 offset += img->cluster_size;
1022 if (update_refcount(fd, img, offset,
1023 offset)) {
1024 fprintf(stderr,
1025 "Programming error: multiple sequential refcount "
1026 "blocks created!\n");
1027 exit(1);
1028 }
1029 }
1030 offset += img->cluster_size;
1031 if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
1032 lseek_error_and_exit(errno);
1033 continue;
1034 }
1035
1036 offset += img->cluster_size;
1037 }
1038 }
1039 update_refcount(fd, img, offset, offset);
1040 flush_l2_cache(img);
1041 sync_refcount(fd, img);
1042
1043 /* Write l1_table*/
1044 if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0)
1045 lseek_error_and_exit(errno);
1046 size = img->l1_size * sizeof(__u64);
1047 generic_write(fd, (char *)img->l1_table, size, 0);
1048
1049 ext2fs_free_mem(&buf);
1050 free_qcow2_image(img);
1051}
1052
1053static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001054{
1055 struct process_block_struct pb;
1056 struct ext2_inode inode;
1057 ext2_inode_scan scan;
1058 ext2_ino_t ino;
1059 errcode_t retval;
1060 char * block_buf;
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001061
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001062 meta_blocks_count = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001063 retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
1064 &meta_block_map);
1065 if (retval) {
1066 com_err(program_name, retval, "while allocating block bitmap");
1067 exit(1);
1068 }
Theodore Ts'od851ed32005-01-19 00:26:43 -05001069
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001070 if (flags & E2IMAGE_SCRAMBLE_FLAG) {
Theodore Ts'od851ed32005-01-19 00:26:43 -05001071 retval = ext2fs_allocate_block_bitmap(fs, "scramble block map",
1072 &scramble_block_map);
1073 if (retval) {
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001074 com_err(program_name, retval,
Theodore Ts'od851ed32005-01-19 00:26:43 -05001075 "while allocating scramble block bitmap");
1076 exit(1);
1077 }
1078 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001079
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001080 mark_table_blocks(fs);
1081
1082 retval = ext2fs_open_inode_scan(fs, 0, &scan);
1083 if (retval) {
1084 com_err(program_name, retval, _("while opening inode scan"));
1085 exit(1);
1086 }
1087
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001088 retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
1089 if (retval) {
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001090 com_err(program_name, 0, "Can't allocate block buffer");
1091 exit(1);
1092 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001093
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -05001094 use_inode_shortcuts(fs, 1);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001095 stashed_inode = &inode;
1096 while (1) {
1097 retval = ext2fs_get_next_inode(scan, &ino, &inode);
Theodore Ts'o3432a912002-10-02 17:47:08 -04001098 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
1099 continue;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001100 if (retval) {
1101 com_err(program_name, retval,
1102 _("while getting next inode"));
1103 exit(1);
1104 }
1105 if (ino == 0)
1106 break;
Theodore Ts'oed909bb2002-08-16 17:03:59 -04001107 if (!inode.i_links_count)
1108 continue;
Theodore Ts'o0c80c442011-10-16 20:29:00 -04001109 if (ext2fs_file_acl_block(fs, &inode)) {
Valerie Aurora Henson3c041a52009-08-22 21:15:30 -04001110 ext2fs_mark_block_bitmap2(meta_block_map,
Theodore Ts'o0c80c442011-10-16 20:29:00 -04001111 ext2fs_file_acl_block(fs, &inode));
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001112 meta_blocks_count++;
Theodore Ts'oed909bb2002-08-16 17:03:59 -04001113 }
Theodore Ts'o0c80c442011-10-16 20:29:00 -04001114 if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001115 continue;
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001116
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001117 stashed_ino = ino;
Theodore Ts'od851ed32005-01-19 00:26:43 -05001118 pb.ino = ino;
1119 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001120 if (LINUX_S_ISDIR(inode.i_mode) ||
Theodore Ts'oeca53e32003-03-14 00:38:45 -05001121 (LINUX_S_ISLNK(inode.i_mode) &&
Theodore Ts'o0c80c442011-10-16 20:29:00 -04001122 ext2fs_inode_has_valid_blocks2(fs, &inode)) ||
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001123 ino == fs->super->s_journal_inum) {
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -04001124 retval = ext2fs_block_iterate3(fs, ino,
Theodore Ts'o43323be2008-02-07 14:37:17 -05001125 BLOCK_FLAG_READ_ONLY, block_buf,
1126 process_dir_block, &pb);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001127 if (retval) {
1128 com_err(program_name, retval,
Eric Sandeend0ff90d2006-09-12 14:56:15 -04001129 "while iterating over inode %u",
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001130 ino);
1131 exit(1);
1132 }
1133 } else {
Theodore Ts'o43323be2008-02-07 14:37:17 -05001134 if ((inode.i_flags & EXT4_EXTENTS_FL) ||
1135 inode.i_block[EXT2_IND_BLOCK] ||
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001136 inode.i_block[EXT2_DIND_BLOCK] ||
1137 inode.i_block[EXT2_TIND_BLOCK]) {
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -04001138 retval = ext2fs_block_iterate3(fs,
Theodore Ts'o43323be2008-02-07 14:37:17 -05001139 ino, BLOCK_FLAG_READ_ONLY, block_buf,
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001140 process_file_block, &pb);
1141 if (retval) {
1142 com_err(program_name, retval,
Eric Sandeend0ff90d2006-09-12 14:56:15 -04001143 "while iterating over inode %u", ino);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001144 exit(1);
1145 }
1146 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001147 }
1148 }
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -05001149 use_inode_shortcuts(fs, 0);
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001150
1151 if (type & E2IMAGE_QCOW2)
1152 output_qcow2_meta_data_blocks(fs, fd);
1153 else
1154 output_meta_data_blocks(fs, fd);
1155
1156 ext2fs_free_mem(&block_buf);
1157 ext2fs_close_inode_scan(scan);
1158 ext2fs_free_block_bitmap(meta_block_map);
1159 if (type & E2IMAGE_SCRAMBLE_FLAG)
1160 ext2fs_free_block_bitmap(scramble_block_map);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001161}
1162
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001163static void install_image(char *device, char *image_fn, int type)
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001164{
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001165 errcode_t retval;
1166 ext2_filsys fs;
1167 int open_flag = EXT2_FLAG_IMAGE_FILE;
1168 int fd = 0;
1169 io_manager io_ptr;
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001170 io_channel io;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001171
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001172 if (type) {
1173 com_err(program_name, 0, "Raw and qcow2 images cannot"
1174 "be installed");
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001175 exit(1);
1176 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001177
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001178#ifdef CONFIG_TESTIO_DEBUG
Theodore Ts'of38cf3c2008-09-01 11:17:29 -04001179 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
1180 io_ptr = test_io_manager;
1181 test_io_backing_manager = unix_io_manager;
1182 } else
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001183#endif
Theodore Ts'of38cf3c2008-09-01 11:17:29 -04001184 io_ptr = unix_io_manager;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001185
1186 retval = ext2fs_open (image_fn, open_flag, 0, 0,
1187 io_ptr, &fs);
1188 if (retval) {
1189 com_err (program_name, retval, _("while trying to open %s"),
1190 image_fn);
1191 exit(1);
1192 }
1193
1194 retval = ext2fs_read_bitmaps (fs);
1195 if (retval) {
1196 com_err(program_name, retval, "error reading bitmaps");
1197 exit(1);
1198 }
1199
Theodore Ts'oc4012e52011-10-06 13:28:07 -04001200 fd = ext2fs_open_file(image_fn, O_RDONLY, 0);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001201 if (fd < 0) {
1202 perror(image_fn);
1203 exit(1);
1204 }
1205
Theodore Ts'oefc6f622008-08-27 23:07:54 -04001206 retval = io_ptr->open(device, IO_FLAG_RW, &io);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001207 if (retval) {
Theodore Ts'o6e82cd72005-01-05 03:02:54 -05001208 com_err(device, 0, "while opening device file");
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001209 exit(1);
1210 }
1211
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001212 ext2fs_rewrite_to_io(fs, io);
1213
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001214 if (ext2fs_llseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) {
1215 perror("ext2fs_llseek");
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001216 exit(1);
1217 }
1218
1219 retval = ext2fs_image_inode_read(fs, fd, 0);
1220 if (retval) {
1221 com_err(image_fn, 0, "while restoring the image table");
1222 exit(1);
1223 }
1224
1225 ext2fs_close (fs);
1226 exit (0);
1227}
1228
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001229static struct ext2_qcow2_hdr *check_qcow2_image(int *fd, char *name)
1230{
1231
Lukas Czernerc859cb12011-09-15 23:44:48 -04001232 *fd = ext2fs_open_file(name, O_RDONLY, 0600);
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001233 if (*fd < 0)
1234 return NULL;
1235
1236 return qcow2_read_header(*fd);
1237}
1238
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001239int main (int argc, char ** argv)
1240{
1241 int c;
1242 errcode_t retval;
1243 ext2_filsys fs;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001244 char *image_fn;
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001245 struct ext2_qcow2_hdr *header = NULL;
Valerie Aurora Hensond991bc72010-06-13 19:00:00 -04001246 int open_flag = EXT2_FLAG_64BITS;
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001247 int img_type = 0;
1248 int flags = 0;
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001249 int qcow2_fd = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001250 int fd = 0;
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001251 int ret = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001252
1253#ifdef ENABLE_NLS
1254 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -05001255 setlocale(LC_CTYPE, "");
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001256 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1257 textdomain(NLS_CAT_NAME);
Theodore Ts'o9d4507c2011-10-05 01:00:30 -04001258 set_com_err_gettext(gettext);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001259#endif
Theodore Ts'o0f8973f2001-08-27 12:44:23 -04001260 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
1261 E2FSPROGS_DATE);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001262 if (argc && *argv)
1263 program_name = *argv;
Theodore Ts'oa6d83022006-12-26 03:38:07 -05001264 add_error_table(&et_ext2_error_table);
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001265 while ((c = getopt(argc, argv, "rsIQ")) != EOF)
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001266 switch (c) {
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001267 case 'I':
1268 flags |= E2IMAGE_INSTALL_FLAG;
1269 break;
1270 case 'Q':
1271 if (img_type)
1272 usage();
1273 img_type |= E2IMAGE_QCOW2;
1274 break;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001275 case 'r':
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001276 if (img_type)
1277 usage();
1278 img_type |= E2IMAGE_RAW;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001279 break;
Theodore Ts'od851ed32005-01-19 00:26:43 -05001280 case 's':
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001281 flags |= E2IMAGE_SCRAMBLE_FLAG;
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001282 break;
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001283 default:
1284 usage();
1285 }
1286 if (optind != argc - 2 )
1287 usage();
1288 device_name = argv[optind];
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001289 image_fn = argv[optind+1];
1290
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001291 if (flags & E2IMAGE_INSTALL_FLAG) {
1292 install_image(device_name, image_fn, img_type);
Theodore Ts'o6e82cd72005-01-05 03:02:54 -05001293 exit (0);
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001294 }
1295
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001296 if (img_type & E2IMAGE_RAW) {
1297 header = check_qcow2_image(&qcow2_fd, device_name);
1298 if (header) {
1299 flags |= E2IMAGE_IS_QCOW2_FLAG;
1300 goto skip_device;
1301 }
1302 }
1303
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001304 retval = ext2fs_open (device_name, open_flag, 0, 0,
1305 unix_io_manager, &fs);
1306 if (retval) {
1307 com_err (program_name, retval, _("while trying to open %s"),
1308 device_name);
Theodore Ts'o54434922003-12-07 01:28:50 -05001309 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001310 exit(1);
1311 }
1312
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001313skip_device:
Theodore Ts'o8c6b6482004-07-28 21:07:53 -04001314 if (strcmp(image_fn, "-") == 0)
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001315 fd = 1;
1316 else {
Lukas Czernerc859cb12011-09-15 23:44:48 -04001317 fd = ext2fs_open_file(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
Theodore Ts'o1c1e0042001-08-09 06:04:32 -04001318 if (fd < 0) {
1319 com_err(program_name, errno,
1320 _("while trying to open %s"), argv[optind+1]);
1321 exit(1);
1322 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001323 }
1324
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001325 if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) {
1326 com_err(program_name, 0, "QCOW2 image can not be written to "
1327 "the stdout!\n");
1328 exit(1);
1329 }
1330
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001331 if (flags & E2IMAGE_IS_QCOW2_FLAG) {
1332 ret = qcow2_write_raw_image(qcow2_fd, fd, header);
1333 if (ret) {
1334 if (ret == -QCOW_COMPRESSED)
1335 fprintf(stderr, "Image (%s) is compressed\n",
1336 image_fn);
1337 if (ret == -QCOW_ENCRYPTED)
1338 fprintf(stderr, "Image (%s) is encrypted\n",
1339 image_fn);
1340 com_err(program_name, ret,
1341 _("while trying to convert qcow2 image"
1342 " (%s) into raw image (%s)"),
1343 device_name, image_fn);
1344 }
1345 goto out;
1346 }
1347
1348
Lukas Czernerbf0449b2011-05-18 13:36:53 +02001349 if (img_type)
1350 write_raw_image_file(fs, fd, img_type, flags);
Theodore Ts'o6304baf2001-08-09 05:41:29 -04001351 else
1352 write_image_file(fs, fd);
Theodore Ts'oc5423c52001-02-08 05:45:17 +00001353
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001354 ext2fs_close (fs);
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001355out:
1356 if (header)
1357 free(header);
1358 if (qcow2_fd)
1359 close(qcow2_fd);
Theodore Ts'oa6d83022006-12-26 03:38:07 -05001360 remove_error_table(&et_ext2_error_table);
Lukas Czerner92dcfb72011-05-18 14:20:47 +02001361 return ret;
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001362}