blob: 8c07b10cc2880bdfe3824d2685d3633225d29351 [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,
243 block + j + 1);
244 }
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);
391 if (retval) {
392 com_err(program_name, retval,
393 _("while getting next inode"));
394 exit(1);
395 }
396 if (ino == 0)
397 break;
398 if (!inode.i_links_count ||
399 !ext2fs_inode_has_valid_blocks(&inode))
400 continue;
401
402 stashed_ino = ino;
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400403 if (LINUX_S_ISDIR(inode.i_mode) ||
404 ino == fs->super->s_journal_inum) {
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400405 retval = ext2fs_block_iterate2(fs, ino, 0,
406 block_buf, process_dir_block, &pb);
407 if (retval) {
408 com_err(program_name, retval,
409 "while iterating over inode %d",
410 ino);
411 exit(1);
412 }
413 } else {
414 if (inode.i_block[EXT2_IND_BLOCK] ||
415 inode.i_block[EXT2_DIND_BLOCK] ||
416 inode.i_block[EXT2_TIND_BLOCK]) {
417 retval = ext2fs_block_iterate2(fs,
418 ino, 0, block_buf,
419 process_file_block, &pb);
420 if (retval) {
421 com_err(program_name, retval,
422 "while iterating over %d", ino);
423 exit(1);
424 }
425 }
426 if (inode.i_file_acl) {
427 ext2fs_mark_block_bitmap(meta_block_map,
428 inode.i_file_acl);
429 }
430 }
431 }
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500432 use_inode_shortcuts(fs, 0);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400433 output_meta_data_blocks(fs, fd);
434}
435
436int main (int argc, char ** argv)
437{
438 int c;
439 errcode_t retval;
440 ext2_filsys fs;
441 char *outfn;
442 int open_flag = 0;
443 int raw_flag = 0;
444 int fd = 0;
445
446#ifdef ENABLE_NLS
447 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -0500448 setlocale(LC_CTYPE, "");
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400449 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
450 textdomain(NLS_CAT_NAME);
451#endif
Theodore Ts'o0f8973f2001-08-27 12:44:23 -0400452 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
453 E2FSPROGS_DATE);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400454 if (argc && *argv)
455 program_name = *argv;
456 initialize_ext2_error_table();
457 while ((c = getopt (argc, argv, "r")) != EOF)
458 switch (c) {
459 case 'r':
460 raw_flag++;
461 break;
462 default:
463 usage();
464 }
465 if (optind != argc - 2 )
466 usage();
467 device_name = argv[optind];
468 outfn = argv[optind+1];
469 retval = ext2fs_open (device_name, open_flag, 0, 0,
470 unix_io_manager, &fs);
471 if (retval) {
472 com_err (program_name, retval, _("while trying to open %s"),
473 device_name);
474 printf(_("Couldn't find valid filesystem superblock.\n"));
475 exit(1);
476 }
477
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400478 if (strcmp(outfn, "-") == 0)
479 fd = 1;
480 else {
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400481#ifdef HAVE_OPEN64
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400482 fd = open64(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400483#else
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400484 fd = open(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400485#endif
Theodore Ts'o1c1e0042001-08-09 06:04:32 -0400486 if (fd < 0) {
487 com_err(program_name, errno,
488 _("while trying to open %s"), argv[optind+1]);
489 exit(1);
490 }
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400491 }
492
493 if (raw_flag)
494 write_raw_image_file(fs, fd);
495 else
496 write_image_file(fs, fd);
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000497
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000498 ext2fs_close (fs);
499 exit (0);
500}