2 * e2undo.c - Replay an undo log onto an ext2/3/4 filesystem
4 * Copyright IBM Corporation, 2007
5 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
8 * This file may be redistributed under the terms of the GNU Public
25 #include "ext2fs/ext2fs.h"
26 #include "support/nls-enable.h"
31 # define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
33 # define dbg_printf(f, a...)
37 * Undo file format: The file is cut up into undo_header.block_size blocks.
38 * The first block contains the header.
39 * The second block contains the superblock.
40 * There is then a repeating series of blocks as follows:
41 * A key block, which contains undo_keys to map the following data blocks.
43 * (Note that there are pointers to the first key block and the sb, so this
44 * order isn't strictly necessary.)
46 #define E2UNDO_MAGIC "E2UNDO02"
47 #define KEYBLOCK_MAGIC 0xCADECADE
49 #define E2UNDO_STATE_FINISHED 0x1 /* undo file is complete */
51 #define E2UNDO_MIN_BLOCK_SIZE 1024 /* undo blocks are no less than 1KB */
52 #define E2UNDO_MAX_BLOCK_SIZE 1048576 /* undo blocks are no more than 1MB */
55 char magic
[8]; /* "E2UNDO02" */
56 __le64 num_keys
; /* how many keys? */
57 __le64 super_offset
; /* where in the file is the superblock copy? */
58 __le64 key_offset
; /* where do the key/data block chunks start? */
59 __le32 block_size
; /* block size of the undo file */
60 __le32 fs_block_size
; /* block size of the target device */
61 __le32 sb_crc
; /* crc32c of the superblock */
62 __le32 state
; /* e2undo state flags */
63 __le32 f_compat
; /* compatible features (none so far) */
64 __le32 f_incompat
; /* incompatible features (none so far) */
65 __le32 f_rocompat
; /* ro compatible features (none so far) */
66 __le32 pad32
; /* padding for fs_offset */
67 __le64 fs_offset
; /* filesystem offset */
68 __u8 padding
[436]; /* padding */
69 __le32 header_crc
; /* crc32c of the header (but not this field) */
72 #define E2UNDO_MAX_EXTENT_BLOCKS 512 /* max extent size, in blocks */
75 __le64 fsblk
; /* where in the fs does the block go */
76 __le32 blk_crc
; /* crc32c of the block */
77 __le32 size
; /* how many bytes in this block? */
80 struct undo_key_block
{
81 __le32 magic
; /* KEYBLOCK_MAGIC number */
82 __le32 crc
; /* block checksum */
83 __le64 reserved
; /* zero */
84 #if __GNUC_PREREQ (4, 8)
85 #pragma GCC diagnostic push
86 #pragma GCC diagnostic ignored "-Wpedantic"
88 struct undo_key keys
[0]; /* keys, which come immediately after */
89 #if __GNUC_PREREQ (4, 8)
90 #pragma GCC diagnostic pop
94 struct undo_key_info
{
101 struct undo_context
{
102 struct undo_header hdr
;
103 io_channel undo_file
;
104 unsigned int blocksize
, fs_blocksize
;
107 struct undo_key_info
*keys
;
109 #define KEYS_PER_BLOCK(d) (((d)->blocksize / sizeof(struct undo_key)) - 1)
111 #define E2UNDO_FEATURE_COMPAT_FS_OFFSET 0x1 /* the filesystem offset */
113 static inline int e2undo_has_feature_fs_offset(struct undo_header
*header
) {
114 return ext2fs_le32_to_cpu(header
->f_compat
) &
115 E2UNDO_FEATURE_COMPAT_FS_OFFSET
;
118 static char *prg_name
;
119 static char *undo_file
;
121 static void usage(void)
124 _("Usage: %s [-f] [-h] [-n] [-o offset] [-v] [-z undo_file] <transaction file> <filesystem>\n"), prg_name
);
128 static void dump_header(struct undo_header
*hdr
)
130 printf("nr keys:\t%llu\n",
131 (unsigned long long) ext2fs_le64_to_cpu(hdr
->num_keys
));
132 printf("super block:\t%llu\n",
133 (unsigned long long) ext2fs_le64_to_cpu(hdr
->super_offset
));
134 printf("key block:\t%llu\n",
135 (unsigned long long) ext2fs_le64_to_cpu(hdr
->key_offset
));
136 printf("block size:\t%u\n", ext2fs_le32_to_cpu(hdr
->block_size
));
137 printf("fs block size:\t%u\n", ext2fs_le32_to_cpu(hdr
->fs_block_size
));
138 printf("super crc:\t0x%x\n", ext2fs_le32_to_cpu(hdr
->sb_crc
));
139 printf("state:\t\t0x%x\n", ext2fs_le32_to_cpu(hdr
->state
));
140 printf("compat:\t\t0x%x\n", ext2fs_le32_to_cpu(hdr
->f_compat
));
141 printf("incompat:\t0x%x\n", ext2fs_le32_to_cpu(hdr
->f_incompat
));
142 printf("rocompat:\t0x%x\n", ext2fs_le32_to_cpu(hdr
->f_rocompat
));
143 if (e2undo_has_feature_fs_offset(hdr
))
144 printf("fs offset:\t%llu\n",
145 (unsigned long long) ext2fs_le64_to_cpu(hdr
->fs_offset
));
146 printf("header crc:\t0x%x\n", ext2fs_le32_to_cpu(hdr
->header_crc
));
149 static void print_undo_mismatch(struct ext2_super_block
*fs_super
,
150 struct ext2_super_block
*undo_super
)
153 _("The file system superblock doesn't match the undo file.\n"));
154 if (memcmp(fs_super
->s_uuid
, undo_super
->s_uuid
,
155 sizeof(fs_super
->s_uuid
)))
156 printf("%s", _("UUID does not match.\n"));
157 if (fs_super
->s_mtime
!= undo_super
->s_mtime
)
158 printf("%s", _("Last mount time does not match.\n"));
159 if (fs_super
->s_wtime
!= undo_super
->s_wtime
)
160 printf("%s", _("Last write time does not match.\n"));
161 if (fs_super
->s_kbytes_written
!= undo_super
->s_kbytes_written
)
162 printf("%s", _("Lifetime write counter does not match.\n"));
165 static int check_filesystem(struct undo_context
*ctx
, io_channel channel
)
167 struct ext2_super_block super
, *sb
;
172 io_channel_set_blksize(channel
, SUPERBLOCK_OFFSET
);
173 retval
= io_channel_read_blk64(channel
, 1, -SUPERBLOCK_SIZE
, &super
);
175 com_err(prg_name
, retval
,
176 "%s", _("while reading filesystem superblock."));
181 * Compare the FS and the undo file superblock so that we can't apply
182 * e2undo "patches" out of order.
184 retval
= ext2fs_get_mem(ctx
->blocksize
, &buf
);
186 com_err(prg_name
, retval
, "%s", _("while allocating memory"));
189 retval
= io_channel_read_blk64(ctx
->undo_file
, ctx
->super_block
,
190 -SUPERBLOCK_SIZE
, buf
);
192 com_err(prg_name
, retval
, "%s", _("while fetching superblock"));
195 sb
= (struct ext2_super_block
*)buf
;
196 sb
->s_magic
= ~sb
->s_magic
;
197 if (memcmp(&super
, buf
, sizeof(super
))) {
198 print_undo_mismatch(&super
, (struct ext2_super_block
*)buf
);
202 sb_crc
= ext2fs_crc32c_le(~0, (unsigned char *)buf
, SUPERBLOCK_SIZE
);
203 if (ext2fs_le32_to_cpu(ctx
->hdr
.sb_crc
) != sb_crc
) {
205 _("Undo file superblock checksum doesn't match.\n"));
211 ext2fs_free_mem(&buf
);
215 static int key_compare(const void *a
, const void *b
)
217 const struct undo_key_info
*ka
, *kb
;
221 return ka
->fsblk
- kb
->fsblk
;
224 static int e2undo_setup_tdb(const char *name
, io_manager
*io_ptr
)
226 errcode_t retval
= 0;
228 char *tdb_file
= NULL
;
229 char *dev_name
, *tmp_name
;
231 /* (re)open a specific undo file */
232 if (undo_file
&& undo_file
[0] != 0) {
233 retval
= set_undo_io_backing_manager(*io_ptr
);
236 *io_ptr
= undo_io_manager
;
237 retval
= set_undo_io_backup_file(undo_file
);
240 printf(_("Overwriting existing filesystem; this can be undone "
241 "using the command:\n"
242 " e2undo %s %s\n\n"),
248 * Configuration via a conf file would be
251 tdb_dir
= getenv("E2FSPROGS_UNDO_DIR");
253 tdb_dir
= "/var/lib/e2fsprogs";
255 if (!strcmp(tdb_dir
, "none") || (tdb_dir
[0] == 0) ||
256 access(tdb_dir
, W_OK
))
259 tmp_name
= strdup(name
);
262 dev_name
= basename(tmp_name
);
263 tdb_file
= malloc(strlen(tdb_dir
) + 8 + strlen(dev_name
) + 7 + 1);
268 sprintf(tdb_file
, "%s/e2undo-%s.e2undo", tdb_dir
, dev_name
);
271 if ((unlink(tdb_file
) < 0) && (errno
!= ENOENT
)) {
273 com_err(prg_name
, retval
,
274 _("while trying to delete %s"), tdb_file
);
278 retval
= set_undo_io_backing_manager(*io_ptr
);
281 *io_ptr
= undo_io_manager
;
282 retval
= set_undo_io_backup_file(tdb_file
);
285 printf(_("Overwriting existing filesystem; this can be undone "
286 "using the command:\n"
287 " e2undo %s %s\n\n"),
295 com_err(prg_name
, retval
, "while trying to setup undo file\n");
299 int main(int argc
, char *argv
[])
301 int c
, force
= 0, dry_run
= 0, verbose
= 0, dump
= 0;
304 int mount_flags
, csum_error
= 0, io_error
= 0;
305 size_t i
, keys_per_block
;
306 char *device_name
, *tdb_file
;
307 io_manager manager
= unix_io_manager
;
308 struct undo_context undo_ctx
;
310 struct undo_key_block
*keyb
;
311 struct undo_key
*dkey
;
312 struct undo_key_info
*ikey
;
313 __u32 key_crc
, blk_crc
, hdr_crc
;
317 char opt_offset_string
[40] = { 0 };
320 setlocale(LC_MESSAGES
, "");
321 setlocale(LC_CTYPE
, "");
322 bindtextdomain(NLS_CAT_NAME
, LOCALEDIR
);
323 textdomain(NLS_CAT_NAME
);
324 set_com_err_gettext(gettext
);
326 add_error_table(&et_ext2_error_table
);
329 while ((c
= getopt(argc
, argv
, "fhno:vz:")) != EOF
) {
341 offset
= strtoull(optarg
, &buf
, 0);
344 _("illegal offset - %s"), optarg
);
347 /* used to indicate that an offset was specified */
348 opt_offset_string
[0] = 1;
361 if (argc
!= optind
+ 2)
364 tdb_file
= argv
[optind
];
365 device_name
= argv
[optind
+1];
367 if (undo_file
&& strcmp(tdb_file
, undo_file
) == 0) {
368 printf(_("Will not write to an undo file while replaying it.\n"));
372 /* Interpret the undo file */
373 retval
= manager
->open(tdb_file
, IO_FLAG_EXCLUSIVE
,
374 &undo_ctx
.undo_file
);
376 com_err(prg_name
, errno
,
377 _("while opening undo file `%s'\n"), tdb_file
);
380 retval
= io_channel_read_blk64(undo_ctx
.undo_file
, 0,
381 -(int)sizeof(undo_ctx
.hdr
),
384 com_err(prg_name
, retval
, _("while reading undo file"));
387 if (memcmp(undo_ctx
.hdr
.magic
, E2UNDO_MAGIC
,
388 sizeof(undo_ctx
.hdr
.magic
))) {
389 fprintf(stderr
, _("%s: Not an undo file.\n"), tdb_file
);
393 dump_header(&undo_ctx
.hdr
);
396 hdr_crc
= ext2fs_crc32c_le(~0, (unsigned char *)&undo_ctx
.hdr
,
397 sizeof(struct undo_header
) -
399 if (!force
&& ext2fs_le32_to_cpu(undo_ctx
.hdr
.header_crc
) != hdr_crc
) {
400 fprintf(stderr
, _("%s: Header checksum doesn't match.\n"),
404 undo_ctx
.blocksize
= ext2fs_le32_to_cpu(undo_ctx
.hdr
.block_size
);
405 undo_ctx
.fs_blocksize
= ext2fs_le32_to_cpu(undo_ctx
.hdr
.fs_block_size
);
406 if (undo_ctx
.blocksize
== 0 || undo_ctx
.fs_blocksize
== 0) {
407 fprintf(stderr
, _("%s: Corrupt undo file header.\n"), tdb_file
);
410 if (!force
&& undo_ctx
.blocksize
> E2UNDO_MAX_BLOCK_SIZE
) {
411 fprintf(stderr
, _("%s: Undo block size too large.\n"),
415 if (!force
&& undo_ctx
.blocksize
< E2UNDO_MIN_BLOCK_SIZE
) {
416 fprintf(stderr
, _("%s: Undo block size too small.\n"),
420 undo_ctx
.super_block
= ext2fs_le64_to_cpu(undo_ctx
.hdr
.super_offset
);
421 undo_ctx
.num_keys
= ext2fs_le64_to_cpu(undo_ctx
.hdr
.num_keys
);
422 io_channel_set_blksize(undo_ctx
.undo_file
, undo_ctx
.blocksize
);
424 * Do not compare undo_ctx.hdr.f_compat with the available compatible
425 * features set, because a "missing" compatible feature should
426 * not cause any problems.
428 if (!force
&& (undo_ctx
.hdr
.f_incompat
|| undo_ctx
.hdr
.f_rocompat
)) {
429 fprintf(stderr
, _("%s: Unknown undo file feature set.\n"),
435 retval
= ext2fs_check_if_mounted(device_name
, &mount_flags
);
437 com_err(prg_name
, retval
, _("Error while determining whether "
438 "%s is mounted."), device_name
);
442 if (mount_flags
& EXT2_MF_MOUNTED
) {
443 com_err(prg_name
, retval
, "%s", _("e2undo should only be run "
444 "on unmounted filesystems"));
449 retval
= e2undo_setup_tdb(device_name
, &manager
);
454 retval
= manager
->open(device_name
,
455 IO_FLAG_EXCLUSIVE
| (dry_run
? 0 : IO_FLAG_RW
),
458 com_err(prg_name
, retval
,
459 _("while opening `%s'"), device_name
);
463 if (*opt_offset_string
|| e2undo_has_feature_fs_offset(&undo_ctx
.hdr
)) {
464 if (!*opt_offset_string
)
465 offset
= ext2fs_le64_to_cpu(undo_ctx
.hdr
.fs_offset
);
466 retval
= snprintf(opt_offset_string
, sizeof(opt_offset_string
),
467 "offset=%llu", (unsigned long long) offset
);
468 if ((size_t) retval
>= sizeof(opt_offset_string
)) {
469 /* should not happen... */
470 com_err(prg_name
, 0, _("specified offset is too large"));
473 io_channel_set_options(channel
, opt_offset_string
);
476 if (!force
&& check_filesystem(&undo_ctx
, channel
))
479 /* prepare to read keys */
480 retval
= ext2fs_get_mem(sizeof(struct undo_key_info
) * undo_ctx
.num_keys
,
483 com_err(prg_name
, retval
, "%s", _("while allocating memory"));
486 ikey
= undo_ctx
.keys
;
487 retval
= ext2fs_get_mem(undo_ctx
.blocksize
, &keyb
);
489 com_err(prg_name
, retval
, "%s", _("while allocating memory"));
492 retval
= ext2fs_get_mem(E2UNDO_MAX_EXTENT_BLOCKS
* undo_ctx
.blocksize
,
495 com_err(prg_name
, retval
, "%s", _("while allocating memory"));
500 keys_per_block
= KEYS_PER_BLOCK(&undo_ctx
);
501 lblk
= ext2fs_le64_to_cpu(undo_ctx
.hdr
.key_offset
);
502 dbg_printf("nr_keys=%lu, kpb=%zu, blksz=%u\n",
503 undo_ctx
.num_keys
, keys_per_block
, undo_ctx
.blocksize
);
504 for (i
= 0; i
< undo_ctx
.num_keys
; i
+= keys_per_block
) {
508 retval
= io_channel_read_blk64(undo_ctx
.undo_file
,
511 com_err(prg_name
, retval
, "%s", _("while reading keys"));
514 undo_ctx
.num_keys
= i
- 1;
522 ext2fs_le32_to_cpu(keyb
->magic
) != KEYBLOCK_MAGIC
) {
523 fprintf(stderr
, _("%s: wrong key magic at %llu\n"),
524 tdb_file
, (unsigned long long) lblk
);
529 key_crc
= ext2fs_crc32c_le(~0, (unsigned char *)keyb
,
531 if (!force
&& ext2fs_le32_to_cpu(crc
) != key_crc
) {
533 _("%s: key block checksum error at %llu.\n"),
534 tdb_file
, (unsigned long long) lblk
);
538 /* load keys from key block */
540 max_j
= undo_ctx
.num_keys
- i
;
541 if (max_j
> keys_per_block
)
542 max_j
= keys_per_block
;
543 for (j
= 0, dkey
= keyb
->keys
;
545 j
++, ikey
++, dkey
++) {
546 ikey
->fsblk
= ext2fs_le64_to_cpu(dkey
->fsblk
);
547 ikey
->fileblk
= lblk
;
548 ikey
->blk_crc
= ext2fs_le32_to_cpu(dkey
->blk_crc
);
549 ikey
->size
= ext2fs_le32_to_cpu(dkey
->size
);
550 lblk
+= (ikey
->size
+ undo_ctx
.blocksize
- 1) /
553 if (E2UNDO_MAX_EXTENT_BLOCKS
* undo_ctx
.blocksize
<
555 com_err(prg_name
, retval
,
556 _("%s: block %llu is too long."),
558 (unsigned long long) ikey
->fsblk
);
562 /* check each block's crc */
563 retval
= io_channel_read_blk64(undo_ctx
.undo_file
,
568 com_err(prg_name
, retval
,
569 _("while fetching block %llu."),
570 (unsigned long long) ikey
->fileblk
);
577 blk_crc
= ext2fs_crc32c_le(~0, (unsigned char *)buf
,
579 if (blk_crc
!= ikey
->blk_crc
) {
581 _("checksum error in filesystem block "
582 "%llu (undo blk %llu)\n"),
583 (unsigned long long) ikey
->fsblk
,
584 (unsigned long long) ikey
->fileblk
);
591 ext2fs_free_mem(&keyb
);
593 /* sort keys in fs block order */
594 qsort(undo_ctx
.keys
, undo_ctx
.num_keys
, sizeof(struct undo_key_info
),
598 io_channel_set_blksize(channel
, undo_ctx
.fs_blocksize
);
599 for (i
= 0, ikey
= undo_ctx
.keys
; i
< undo_ctx
.num_keys
; i
++, ikey
++) {
600 retval
= io_channel_read_blk64(undo_ctx
.undo_file
,
605 com_err(prg_name
, retval
,
606 _("while fetching block %llu."),
607 (unsigned long long) ikey
->fileblk
);
613 printf("Replayed block of size %u from %llu to %llu\n",
614 ikey
->size
, (unsigned long long) ikey
->fileblk
,
615 (unsigned long long) ikey
->fsblk
);
618 retval
= io_channel_write_blk64(channel
, ikey
->fsblk
,
619 -(int)ikey
->size
, buf
);
621 com_err(prg_name
, retval
,
622 _("while writing block %llu."),
623 (unsigned long long) ikey
->fsblk
);
629 fprintf(stderr
, _("Undo file corruption; run e2fsck NOW!\n"));
631 fprintf(stderr
, _("IO error during replay; run e2fsck NOW!\n"));
632 if (!(ext2fs_le32_to_cpu(undo_ctx
.hdr
.state
) & E2UNDO_STATE_FINISHED
)) {
634 fprintf(stderr
, _("Incomplete undo record; run e2fsck.\n"));
636 ext2fs_free_mem(&buf
);
637 ext2fs_free_mem(&undo_ctx
.keys
);
638 io_channel_close(channel
);
640 /* If there were problems, try to force a fsck */
641 if (!dry_run
&& (force
|| csum_error
|| io_error
)) {
642 retval
= ext2fs_open2(device_name
, NULL
,
643 EXT2_FLAG_RW
| EXT2_FLAG_64BITS
, 0, 0,
647 fs
->super
->s_state
&= ~EXT2_VALID_FS
;
648 if (csum_error
|| io_error
)
649 fs
->super
->s_state
|= EXT2_ERROR_FS
;
650 ext2fs_mark_super_dirty(fs
);
651 ext2fs_close_free(&fs
);
655 io_channel_close(undo_ctx
.undo_file
);