blob: f692170d855982a2d25d6e84cb31b16435e78d57 [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>
26#include <stdlib.h>
27#include <string.h>
28#include <time.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34
Theodore Ts'o54c637d2001-05-14 11:45:38 +000035#include "ext2fs/ext2_fs.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000036#include "ext2fs/ext2fs.h"
37#include "et/com_err.h"
38#include "uuid/uuid.h"
39#include "e2p/e2p.h"
40#include "ext2fs/e2image.h"
41
42#include "../version.h"
43#include "nls-enable.h"
44
Theodore Ts'o3e377db2000-12-09 02:37:33 +000045const char * program_name = "e2image";
Theodore Ts'o72ed1262000-11-12 19:32:20 +000046char * device_name = NULL;
47
48static void usage(void)
49{
Theodore Ts'o6304baf2001-08-09 05:41:29 -040050 fprintf(stderr, _("Usage: %s [-r] device file\n"), program_name);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000051 exit (1);
52}
53
Theodore Ts'o095b4592001-05-03 13:33:11 +000054static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize)
Theodore Ts'o72ed1262000-11-12 19:32:20 +000055{
Theodore Ts'o095b4592001-05-03 13:33:11 +000056 char *header_buf;
Theodore Ts'o72ed1262000-11-12 19:32:20 +000057 int actual;
58
Theodore Ts'o095b4592001-05-03 13:33:11 +000059 header_buf = malloc(blocksize);
60 if (!header_buf) {
61 fprintf(stderr, _("Couldn't allocate header buffer\n"));
62 exit(1);
63 }
64
Theodore Ts'o72ed1262000-11-12 19:32:20 +000065 if (lseek(fd, 0, SEEK_SET) < 0) {
66 perror("lseek while writing header");
67 exit(1);
68 }
Theodore Ts'o095b4592001-05-03 13:33:11 +000069 memset(header_buf, 0, blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000070
71 if (hdr)
72 memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
73
Theodore Ts'o095b4592001-05-03 13:33:11 +000074 actual = write(fd, header_buf, blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000075 if (actual < 0) {
76 perror("write header");
77 exit(1);
78 }
Theodore Ts'o095b4592001-05-03 13:33:11 +000079 if (actual != blocksize) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +000080 fprintf(stderr, _("short write (only %d bytes) for"
81 "writing image header"), actual);
82 exit(1);
83 }
Theodore Ts'o095b4592001-05-03 13:33:11 +000084 free(header_buf);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000085}
86
Theodore Ts'o6304baf2001-08-09 05:41:29 -040087static void write_image_file(ext2_filsys fs, int fd)
Theodore Ts'o72ed1262000-11-12 19:32:20 +000088{
Theodore Ts'o6304baf2001-08-09 05:41:29 -040089 struct ext2_image_hdr hdr;
90 struct stat st;
91 errcode_t retval;
Theodore Ts'o72ed1262000-11-12 19:32:20 +000092
Theodore Ts'o095b4592001-05-03 13:33:11 +000093 write_header(fd, NULL, fs->blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000094 memset(&hdr, 0, sizeof(struct ext2_image_hdr));
Theodore Ts'o6304baf2001-08-09 05:41:29 -040095
Theodore Ts'o72ed1262000-11-12 19:32:20 +000096 hdr.offset_super = lseek(fd, 0, SEEK_CUR);
97 retval = ext2fs_image_super_write(fs, fd, 0);
98 if (retval) {
99 com_err(program_name, retval, _("while writing superblock"));
100 exit(1);
101 }
102
103 hdr.offset_inode = lseek(fd, 0, SEEK_CUR);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400104 retval = ext2fs_image_inode_write(fs, fd,
105 (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000106 if (retval) {
107 com_err(program_name, retval, _("while writing inode table"));
108 exit(1);
109 }
110
111 hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR);
112 retval = ext2fs_image_bitmap_write(fs, fd, 0);
113 if (retval) {
114 com_err(program_name, retval, _("while writing block bitmap"));
115 exit(1);
116 }
117
118 hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR);
119 retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
120 if (retval) {
121 com_err(program_name, retval, _("while writing inode bitmap"));
122 exit(1);
123 }
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000124
125 hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
126 strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
127 gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
Theodore Ts'o095b4592001-05-03 13:33:11 +0000128 strncat(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name));
129 hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
130 hdr.fs_blocksize = fs->blocksize;
131
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000132 if (stat(device_name, &st) == 0)
133 hdr.fs_device = st.st_rdev;
134
135 if (fstat(fd, &st) == 0) {
136 hdr.image_device = st.st_dev;
137 hdr.image_inode = st.st_ino;
138 }
139 memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
140
141 hdr.image_time = time(0);
Theodore Ts'o095b4592001-05-03 13:33:11 +0000142 write_header(fd, &hdr, fs->blocksize);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400143}
144
145/*
146 * These set of functions are used to write a RAW image file.
147 */
148ext2fs_block_bitmap meta_block_map;
149
150struct process_block_struct {
151 ext2_ino_t ino;
152};
153
154/*
155 * These subroutines short circuits ext2fs_get_blocks and
156 * ext2fs_check_directory; we use them since we already have the inode
157 * structure, so there's no point in letting the ext2fs library read
158 * the inode again.
159 */
160static ino_t stashed_ino = 0;
161static struct ext2_inode *stashed_inode;
162
163static errcode_t meta_get_blocks(ext2_filsys fs, ext2_ino_t ino,
164 blk_t *blocks)
165{
166 int i;
167
168 if ((ino != stashed_ino) || !stashed_inode)
169 return EXT2_ET_CALLBACK_NOTHANDLED;
170
171 for (i=0; i < EXT2_N_BLOCKS; i++)
172 blocks[i] = stashed_inode->i_block[i];
173 return 0;
174}
175
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500176static errcode_t meta_check_directory(ext2_filsys fs, ext2_ino_t ino)
177{
178 if ((ino != stashed_ino) || !stashed_inode)
179 return EXT2_ET_CALLBACK_NOTHANDLED;
180
181 if (!LINUX_S_ISDIR(stashed_inode->i_mode))
182 return EXT2_ET_NO_DIRECTORY;
183 return 0;
184}
185
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400186static errcode_t meta_read_inode(ext2_filsys fs, ext2_ino_t ino,
187 struct ext2_inode *inode)
188{
189 if ((ino != stashed_ino) || !stashed_inode)
190 return EXT2_ET_CALLBACK_NOTHANDLED;
191 *inode = *stashed_inode;
192 return 0;
193}
194
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500195static void use_inode_shortcuts(ext2_filsys fs, int bool)
196{
197 if (bool) {
198 fs->get_blocks = meta_get_blocks;
199 fs->check_directory = meta_check_directory;
200 fs->read_inode = meta_read_inode;
201 stashed_ino = 0;
202 } else {
203 fs->get_blocks = 0;
204 fs->check_directory = 0;
205 fs->read_inode = 0;
206 }
207}
208
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400209static int process_dir_block(ext2_filsys fs, blk_t *block_nr,
210 e2_blkcnt_t blockcnt, blk_t ref_block,
211 int ref_offset, void *priv_data)
212{
213 ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
214 return 0;
215}
216
217static int process_file_block(ext2_filsys fs, blk_t *block_nr,
218 e2_blkcnt_t blockcnt, blk_t ref_block,
219 int ref_offset, void *priv_data)
220{
221 if (blockcnt < 0) {
222 ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
223 }
224 return 0;
225}
226
227static void mark_table_blocks(ext2_filsys fs)
228{
229 blk_t block, b;
230 int i,j;
231
232 block = fs->super->s_first_data_block;
233 /*
234 * Mark primary superblock
235 */
236 ext2fs_mark_block_bitmap(meta_block_map, block);
237
238 /*
239 * Mark the primary superblock descriptors
240 */
241 for (j = 0; j < fs->desc_blocks; j++) {
242 ext2fs_mark_block_bitmap(meta_block_map,
Theodore Ts'oc046ac72002-10-20 00:38:57 -0400243 ext2fs_descriptor_block_loc(fs, block, j));
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400244 }
245
246 for (i = 0; i < fs->group_desc_count; i++) {
247 /*
248 * Mark the blocks used for the inode table
249 */
250 if (fs->group_desc[i].bg_inode_table) {
251 for (j = 0, b = fs->group_desc[i].bg_inode_table;
252 j < fs->inode_blocks_per_group;
253 j++, b++)
254 ext2fs_mark_block_bitmap(meta_block_map, b);
255 }
256
257 /*
258 * Mark block used for the block bitmap
259 */
260 if (fs->group_desc[i].bg_block_bitmap) {
261 ext2fs_mark_block_bitmap(meta_block_map,
262 fs->group_desc[i].bg_block_bitmap);
263 }
264
265 /*
266 * Mark block used for the inode bitmap
267 */
268 if (fs->group_desc[i].bg_inode_bitmap) {
269 ext2fs_mark_block_bitmap(meta_block_map,
270 fs->group_desc[i].bg_inode_bitmap);
271 }
272 block += fs->super->s_blocks_per_group;
273 }
274}
275
276/*
277 * This function returns 1 if the specified block is all zeros
278 */
279static int check_zero_block(char *buf, int blocksize)
280{
281 char *cp = buf;
282 int left = blocksize;
283
284 while (left > 0) {
285 if (*cp++)
286 return 0;
287 left--;
288 }
289 return 1;
290}
291
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500292static void write_block(int fd, char *buf, int sparse_offset,
293 int blocksize, blk_t block)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400294{
295 int count;
296 errcode_t err;
297
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500298 if (sparse_offset) {
299#ifdef HAVE_LSEEK64
300 if (lseek64(fd, sparse_offset, SEEK_CUR) < 0)
301 perror("lseek");
302#else
303 if (lseek(fd, sparse_offset, SEEK_CUR) < 0)
304 perror("lseek");
305#endif
306 }
307 if (blocksize) {
308 count = write(fd, buf, blocksize);
309 if (count != blocksize) {
310 if (count == -1)
311 err = errno;
312 else
313 err = 0;
314 com_err(program_name, err, "error writing block %d",
315 block);
316 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400317 }
318}
319
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500320static void output_meta_data_blocks(ext2_filsys fs, int fd)
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400321{
322 errcode_t retval;
323 blk_t blk;
324 char buf[8192], zero_buf[8192];
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500325 int sparse = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400326
327 memset(zero_buf, 0, sizeof(zero_buf));
328 for (blk = 0; blk < fs->super->s_blocks_count; blk++) {
329 if ((blk >= fs->super->s_first_data_block) &&
330 ext2fs_test_block_bitmap(meta_block_map, blk)) {
331 retval = io_channel_read_blk(fs->io, blk, 1, buf);
332 if (retval) {
333 com_err(program_name, retval,
334 "error reading block %d", blk);
335 }
336 if ((fd != 1) && check_zero_block(buf, fs->blocksize))
337 goto sparse_write;
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500338 write_block(fd, buf, sparse, fs->blocksize, blk);
339 sparse = 0;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400340 } else {
341 sparse_write:
342 if (fd == 1) {
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500343 write_block(fd, zero_buf, 0,
344 fs->blocksize, blk);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400345 continue;
346 }
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500347 sparse += fs->blocksize;
348 if (sparse >= 1024*1024) {
349 write_block(fd, 0, sparse, 0, 0);
350 sparse = 0;
351 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400352 }
353 }
Theodore Ts'oe3ef3502001-11-05 18:57:43 -0500354 write_block(fd, zero_buf, sparse, 1, -1);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400355}
356
357static void write_raw_image_file(ext2_filsys fs, int fd)
358{
359 struct process_block_struct pb;
360 struct ext2_inode inode;
361 ext2_inode_scan scan;
362 ext2_ino_t ino;
363 errcode_t retval;
364 char * block_buf;
365
366 retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
367 &meta_block_map);
368 if (retval) {
369 com_err(program_name, retval, "while allocating block bitmap");
370 exit(1);
371 }
372
373 mark_table_blocks(fs);
374
375 retval = ext2fs_open_inode_scan(fs, 0, &scan);
376 if (retval) {
377 com_err(program_name, retval, _("while opening inode scan"));
378 exit(1);
379 }
380
381 block_buf = malloc(fs->blocksize * 3);
382 if (!block_buf) {
383 com_err(program_name, 0, "Can't allocate block buffer");
384 exit(1);
385 }
386
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500387 use_inode_shortcuts(fs, 1);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400388 stashed_inode = &inode;
389 while (1) {
390 retval = ext2fs_get_next_inode(scan, &ino, &inode);
Theodore Ts'o3432a912002-10-02 17:47:08 -0400391 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
392 continue;
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400393 if (retval) {
394 com_err(program_name, retval,
395 _("while getting next inode"));
396 exit(1);
397 }
398 if (ino == 0)
399 break;
Theodore Ts'oed909bb2002-08-16 17:03:59 -0400400 if (!inode.i_links_count)
401 continue;
402 if (inode.i_file_acl) {
403 ext2fs_mark_block_bitmap(meta_block_map,
404 inode.i_file_acl);
405 }
406 if (!ext2fs_inode_has_valid_blocks(&inode))
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400407 continue;
408
409 stashed_ino = ino;
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400410 if (LINUX_S_ISDIR(inode.i_mode) ||
Theodore Ts'oeca53e32003-03-14 00:38:45 -0500411 (LINUX_S_ISLNK(inode.i_mode) &&
412 ext2fs_inode_has_valid_blocks(&inode)) ||
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400413 ino == fs->super->s_journal_inum) {
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400414 retval = ext2fs_block_iterate2(fs, ino, 0,
415 block_buf, process_dir_block, &pb);
416 if (retval) {
417 com_err(program_name, retval,
418 "while iterating over inode %d",
419 ino);
420 exit(1);
421 }
422 } else {
423 if (inode.i_block[EXT2_IND_BLOCK] ||
424 inode.i_block[EXT2_DIND_BLOCK] ||
425 inode.i_block[EXT2_TIND_BLOCK]) {
426 retval = ext2fs_block_iterate2(fs,
427 ino, 0, block_buf,
428 process_file_block, &pb);
429 if (retval) {
430 com_err(program_name, retval,
431 "while iterating over %d", ino);
432 exit(1);
433 }
434 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400435 }
436 }
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500437 use_inode_shortcuts(fs, 0);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400438 output_meta_data_blocks(fs, fd);
439}
440
441int main (int argc, char ** argv)
442{
443 int c;
444 errcode_t retval;
445 ext2_filsys fs;
446 char *outfn;
447 int open_flag = 0;
448 int raw_flag = 0;
449 int fd = 0;
450
451#ifdef ENABLE_NLS
452 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -0500453 setlocale(LC_CTYPE, "");
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400454 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
455 textdomain(NLS_CAT_NAME);
456#endif
Theodore Ts'o0f8973f2001-08-27 12:44:23 -0400457 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
458 E2FSPROGS_DATE);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400459 if (argc && *argv)
460 program_name = *argv;
461 initialize_ext2_error_table();
462 while ((c = getopt (argc, argv, "r")) != EOF)
463 switch (c) {
464 case 'r':
465 raw_flag++;
466 break;
467 default:
468 usage();
469 }
470 if (optind != argc - 2 )
471 usage();
472 device_name = argv[optind];
473 outfn = argv[optind+1];
474 retval = ext2fs_open (device_name, open_flag, 0, 0,
475 unix_io_manager, &fs);
476 if (retval) {
477 com_err (program_name, retval, _("while trying to open %s"),
478 device_name);
479 printf(_("Couldn't find valid filesystem superblock.\n"));
480 exit(1);
481 }
482
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400483 if (strcmp(outfn, "-") == 0)
484 fd = 1;
485 else {
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400486#ifdef HAVE_OPEN64
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400487 fd = open64(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400488#else
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400489 fd = open(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400490#endif
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400491 if (fd < 0) {
492 com_err(program_name, errno,
493 _("while trying to open %s"), argv[optind+1]);
494 exit(1);
495 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400496 }
497
498 if (raw_flag)
499 write_raw_image_file(fs, fd);
500 else
501 write_image_file(fs, fd);
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000502
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000503 ext2fs_close (fs);
504 exit (0);
505}