2 * e2image.c --- Program which writes an image file backing up
3 * critical metadata for the filesystem.
5 * Copyright 2000, 2001 by Theodore Ts'o.
8 * This file may be redistributed under the terms of the GNU Public
13 #define _LARGEFILE_SOURCE
14 #define _LARGEFILE64_SOURCE
33 #include <sys/types.h>
35 #include "ext2fs/ext2_fs.h"
36 #include "ext2fs/ext2fs.h"
37 #include "et/com_err.h"
38 #include "uuid/uuid.h"
40 #include "ext2fs/e2image.h"
42 #include "../version.h"
43 #include "nls-enable.h"
45 const char * program_name
= "e2image";
46 char * device_name
= NULL
;
48 static void usage(void)
50 fprintf(stderr
, _("Usage: %s [-r] device file\n"), program_name
);
54 static void write_header(int fd
, struct ext2_image_hdr
*hdr
, int blocksize
)
59 header_buf
= malloc(blocksize
);
61 fputs(_("Couldn't allocate header buffer\n"), stderr
);
65 if (lseek(fd
, 0, SEEK_SET
) < 0) {
66 perror("lseek while writing header");
69 memset(header_buf
, 0, blocksize
);
72 memcpy(header_buf
, hdr
, sizeof(struct ext2_image_hdr
));
74 actual
= write(fd
, header_buf
, blocksize
);
76 perror("write header");
79 if (actual
!= blocksize
) {
80 fprintf(stderr
, _("short write (only %d bytes) for "
81 "writing image header"), actual
);
87 static void write_image_file(ext2_filsys fs
, int fd
)
89 struct ext2_image_hdr hdr
;
93 write_header(fd
, NULL
, fs
->blocksize
);
94 memset(&hdr
, 0, sizeof(struct ext2_image_hdr
));
96 hdr
.offset_super
= lseek(fd
, 0, SEEK_CUR
);
97 retval
= ext2fs_image_super_write(fs
, fd
, 0);
99 com_err(program_name
, retval
, _("while writing superblock"));
103 hdr
.offset_inode
= lseek(fd
, 0, SEEK_CUR
);
104 retval
= ext2fs_image_inode_write(fs
, fd
,
105 (fd
!= 1) ? IMAGER_FLAG_SPARSEWRITE
: 0);
107 com_err(program_name
, retval
, _("while writing inode table"));
111 hdr
.offset_blockmap
= lseek(fd
, 0, SEEK_CUR
);
112 retval
= ext2fs_image_bitmap_write(fs
, fd
, 0);
114 com_err(program_name
, retval
, _("while writing block bitmap"));
118 hdr
.offset_inodemap
= lseek(fd
, 0, SEEK_CUR
);
119 retval
= ext2fs_image_bitmap_write(fs
, fd
, IMAGER_FLAG_INODEMAP
);
121 com_err(program_name
, retval
, _("while writing inode bitmap"));
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
));
128 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
;
132 if (stat(device_name
, &st
) == 0)
133 hdr
.fs_device
= st
.st_rdev
;
135 if (fstat(fd
, &st
) == 0) {
136 hdr
.image_device
= st
.st_dev
;
137 hdr
.image_inode
= st
.st_ino
;
139 memcpy(hdr
.fs_uuid
, fs
->super
->s_uuid
, sizeof(hdr
.fs_uuid
));
141 hdr
.image_time
= time(0);
142 write_header(fd
, &hdr
, fs
->blocksize
);
146 * These set of functions are used to write a RAW image file.
148 ext2fs_block_bitmap meta_block_map
;
150 struct process_block_struct
{
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
160 static ino_t stashed_ino
= 0;
161 static struct ext2_inode
*stashed_inode
;
163 static errcode_t
meta_get_blocks(ext2_filsys fs
EXT2FS_ATTR((unused
)),
169 if ((ino
!= stashed_ino
) || !stashed_inode
)
170 return EXT2_ET_CALLBACK_NOTHANDLED
;
172 for (i
=0; i
< EXT2_N_BLOCKS
; i
++)
173 blocks
[i
] = stashed_inode
->i_block
[i
];
177 static errcode_t
meta_check_directory(ext2_filsys fs
EXT2FS_ATTR((unused
)),
180 if ((ino
!= stashed_ino
) || !stashed_inode
)
181 return EXT2_ET_CALLBACK_NOTHANDLED
;
183 if (!LINUX_S_ISDIR(stashed_inode
->i_mode
))
184 return EXT2_ET_NO_DIRECTORY
;
188 static errcode_t
meta_read_inode(ext2_filsys fs
EXT2FS_ATTR((unused
)),
190 struct ext2_inode
*inode
)
192 if ((ino
!= stashed_ino
) || !stashed_inode
)
193 return EXT2_ET_CALLBACK_NOTHANDLED
;
194 *inode
= *stashed_inode
;
198 static void use_inode_shortcuts(ext2_filsys fs
, int bool)
201 fs
->get_blocks
= meta_get_blocks
;
202 fs
->check_directory
= meta_check_directory
;
203 fs
->read_inode
= meta_read_inode
;
207 fs
->check_directory
= 0;
212 static int process_dir_block(ext2_filsys fs
EXT2FS_ATTR((unused
)),
214 e2_blkcnt_t blockcnt
EXT2FS_ATTR((unused
)),
215 blk_t ref_block
EXT2FS_ATTR((unused
)),
216 int ref_offset
EXT2FS_ATTR((unused
)),
217 void *priv_data
EXT2FS_ATTR((unused
)))
219 ext2fs_mark_block_bitmap(meta_block_map
, *block_nr
);
223 static int process_file_block(ext2_filsys fs
EXT2FS_ATTR((unused
)),
225 e2_blkcnt_t blockcnt
,
226 blk_t ref_block
EXT2FS_ATTR((unused
)),
227 int ref_offset
EXT2FS_ATTR((unused
)),
228 void *priv_data
EXT2FS_ATTR((unused
)))
231 ext2fs_mark_block_bitmap(meta_block_map
, *block_nr
);
236 static void mark_table_blocks(ext2_filsys fs
)
241 block
= fs
->super
->s_first_data_block
;
243 * Mark primary superblock
245 ext2fs_mark_block_bitmap(meta_block_map
, block
);
248 * Mark the primary superblock descriptors
250 for (j
= 0; j
< fs
->desc_blocks
; j
++) {
251 ext2fs_mark_block_bitmap(meta_block_map
,
252 ext2fs_descriptor_block_loc(fs
, block
, j
));
255 for (i
= 0; i
< fs
->group_desc_count
; i
++) {
257 * Mark the blocks used for the inode table
259 if (fs
->group_desc
[i
].bg_inode_table
) {
260 for (j
= 0, b
= fs
->group_desc
[i
].bg_inode_table
;
261 j
< (unsigned) fs
->inode_blocks_per_group
;
263 ext2fs_mark_block_bitmap(meta_block_map
, b
);
267 * Mark block used for the block bitmap
269 if (fs
->group_desc
[i
].bg_block_bitmap
) {
270 ext2fs_mark_block_bitmap(meta_block_map
,
271 fs
->group_desc
[i
].bg_block_bitmap
);
275 * Mark block used for the inode bitmap
277 if (fs
->group_desc
[i
].bg_inode_bitmap
) {
278 ext2fs_mark_block_bitmap(meta_block_map
,
279 fs
->group_desc
[i
].bg_inode_bitmap
);
281 block
+= fs
->super
->s_blocks_per_group
;
286 * This function returns 1 if the specified block is all zeros
288 static int check_zero_block(char *buf
, int blocksize
)
291 int left
= blocksize
;
301 static void write_block(int fd
, char *buf
, int sparse_offset
,
302 int blocksize
, blk_t block
)
309 if (lseek64(fd
, sparse_offset
, SEEK_CUR
) < 0)
312 if (lseek(fd
, sparse_offset
, SEEK_CUR
) < 0)
317 count
= write(fd
, buf
, blocksize
);
318 if (count
!= blocksize
) {
323 com_err(program_name
, err
, "error writing block %d",
329 static void output_meta_data_blocks(ext2_filsys fs
, int fd
)
333 char buf
[8192], zero_buf
[8192];
336 memset(zero_buf
, 0, sizeof(zero_buf
));
337 for (blk
= 0; blk
< fs
->super
->s_blocks_count
; blk
++) {
338 if ((blk
>= fs
->super
->s_first_data_block
) &&
339 ext2fs_test_block_bitmap(meta_block_map
, blk
)) {
340 retval
= io_channel_read_blk(fs
->io
, blk
, 1, buf
);
342 com_err(program_name
, retval
,
343 "error reading block %d", blk
);
345 if ((fd
!= 1) && check_zero_block(buf
, fs
->blocksize
))
347 write_block(fd
, buf
, sparse
, fs
->blocksize
, blk
);
352 write_block(fd
, zero_buf
, 0,
356 sparse
+= fs
->blocksize
;
357 if (sparse
>= 1024*1024) {
358 write_block(fd
, 0, sparse
, 0, 0);
363 write_block(fd
, zero_buf
, sparse
, 1, -1);
366 static void write_raw_image_file(ext2_filsys fs
, int fd
)
368 struct process_block_struct pb
;
369 struct ext2_inode inode
;
370 ext2_inode_scan scan
;
375 retval
= ext2fs_allocate_block_bitmap(fs
, "in-use block map",
378 com_err(program_name
, retval
, "while allocating block bitmap");
382 mark_table_blocks(fs
);
384 retval
= ext2fs_open_inode_scan(fs
, 0, &scan
);
386 com_err(program_name
, retval
, _("while opening inode scan"));
390 block_buf
= malloc(fs
->blocksize
* 3);
392 com_err(program_name
, 0, "Can't allocate block buffer");
396 use_inode_shortcuts(fs
, 1);
397 stashed_inode
= &inode
;
399 retval
= ext2fs_get_next_inode(scan
, &ino
, &inode
);
400 if (retval
== EXT2_ET_BAD_BLOCK_IN_INODE_TABLE
)
403 com_err(program_name
, retval
,
404 _("while getting next inode"));
409 if (!inode
.i_links_count
)
411 if (inode
.i_file_acl
) {
412 ext2fs_mark_block_bitmap(meta_block_map
,
415 if (!ext2fs_inode_has_valid_blocks(&inode
))
419 if (LINUX_S_ISDIR(inode
.i_mode
) ||
420 (LINUX_S_ISLNK(inode
.i_mode
) &&
421 ext2fs_inode_has_valid_blocks(&inode
)) ||
422 ino
== fs
->super
->s_journal_inum
) {
423 retval
= ext2fs_block_iterate2(fs
, ino
, 0,
424 block_buf
, process_dir_block
, &pb
);
426 com_err(program_name
, retval
,
427 "while iterating over inode %d",
432 if (inode
.i_block
[EXT2_IND_BLOCK
] ||
433 inode
.i_block
[EXT2_DIND_BLOCK
] ||
434 inode
.i_block
[EXT2_TIND_BLOCK
]) {
435 retval
= ext2fs_block_iterate2(fs
,
437 process_file_block
, &pb
);
439 com_err(program_name
, retval
,
440 "while iterating over %d", ino
);
446 use_inode_shortcuts(fs
, 0);
447 output_meta_data_blocks(fs
, fd
);
450 int main (int argc
, char ** argv
)
461 setlocale(LC_MESSAGES
, "");
462 setlocale(LC_CTYPE
, "");
463 bindtextdomain(NLS_CAT_NAME
, LOCALEDIR
);
464 textdomain(NLS_CAT_NAME
);
466 fprintf (stderr
, "e2image %s (%s)\n", E2FSPROGS_VERSION
,
469 program_name
= *argv
;
470 initialize_ext2_error_table();
471 while ((c
= getopt (argc
, argv
, "r")) != EOF
)
479 if (optind
!= argc
- 2 )
481 device_name
= argv
[optind
];
482 outfn
= argv
[optind
+1];
483 retval
= ext2fs_open (device_name
, open_flag
, 0, 0,
484 unix_io_manager
, &fs
);
486 com_err (program_name
, retval
, _("while trying to open %s"),
488 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout
);
492 if (strcmp(outfn
, "-") == 0)
496 fd
= open64(outfn
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0600);
498 fd
= open(outfn
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0600);
501 com_err(program_name
, errno
,
502 _("while trying to open %s"), argv
[optind
+1]);
508 write_raw_image_file(fs
, fd
);
510 write_image_file(fs
, fd
);