Whamcloud - gitweb
mke2fs.8: Improve valid block size documentation
[tools/e2fsprogs.git] / misc / e2undo.c
1 /*
2  * e2undo.c - Replay an undo log onto an ext2/3/4 filesystem
3  *
4  * Copyright IBM Corporation, 2007
5  * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
6  *
7  * %Begin-Header%
8  * This file may be redistributed under the terms of the GNU Public
9  * License.
10  * %End-Header%
11  */
12
13 #include "config.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #ifdef HAVE_GETOPT_H
17 #include <getopt.h>
18 #endif
19 #include <fcntl.h>
20 #if HAVE_ERRNO_H
21 #include <errno.h>
22 #endif
23 #include <unistd.h>
24 #include <libgen.h>
25 #include "ext2fs/ext2fs.h"
26 #include "support/nls-enable.h"
27
28 #undef DEBUG
29
30 #ifdef DEBUG
31 # define dbg_printf(f, a...)  do {printf(f, ## a); fflush(stdout); } while (0)
32 #else
33 # define dbg_printf(f, a...)
34 #endif
35
36 /*
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.
42  *   Data blocks
43  * (Note that there are pointers to the first key block and the sb, so this
44  * order isn't strictly necessary.)
45  */
46 #define E2UNDO_MAGIC "E2UNDO02"
47 #define KEYBLOCK_MAGIC 0xCADECADE
48
49 #define E2UNDO_STATE_FINISHED   0x1     /* undo file is complete */
50
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 */
53
54 struct undo_header {
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) */
70 };
71
72 #define E2UNDO_MAX_EXTENT_BLOCKS        512     /* max extent size, in blocks */
73
74 struct undo_key {
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? */
78 };
79
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"
87 #endif
88         struct undo_key keys[0];        /* keys, which come immediately after */
89 #if __GNUC_PREREQ (4, 8)
90 #pragma GCC diagnostic pop
91 #endif
92 };
93
94 struct undo_key_info {
95         blk64_t fsblk;
96         blk64_t fileblk;
97         __u32 blk_crc;
98         unsigned int size;
99 };
100
101 struct undo_context {
102         struct undo_header hdr;
103         io_channel undo_file;
104         unsigned int blocksize, fs_blocksize;
105         blk64_t super_block;
106         size_t num_keys;
107         struct undo_key_info *keys;
108 };
109 #define KEYS_PER_BLOCK(d) (((d)->blocksize / sizeof(struct undo_key)) - 1)
110
111 #define E2UNDO_FEATURE_COMPAT_FS_OFFSET 0x1     /* the filesystem offset */
112
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;
116 }
117
118 static char *prg_name;
119 static char *undo_file;
120
121 static void usage(void)
122 {
123         fprintf(stderr,
124                 _("Usage: %s [-f] [-h] [-n] [-o offset] [-v] [-z undo_file] <transaction file> <filesystem>\n"), prg_name);
125         exit(1);
126 }
127
128 static void dump_header(struct undo_header *hdr)
129 {
130         printf("nr keys:\t%llu\n", ext2fs_le64_to_cpu(hdr->num_keys));
131         printf("super block:\t%llu\n", ext2fs_le64_to_cpu(hdr->super_offset));
132         printf("key block:\t%llu\n", ext2fs_le64_to_cpu(hdr->key_offset));
133         printf("block size:\t%u\n", ext2fs_le32_to_cpu(hdr->block_size));
134         printf("fs block size:\t%u\n", ext2fs_le32_to_cpu(hdr->fs_block_size));
135         printf("super crc:\t0x%x\n", ext2fs_le32_to_cpu(hdr->sb_crc));
136         printf("state:\t\t0x%x\n", ext2fs_le32_to_cpu(hdr->state));
137         printf("compat:\t\t0x%x\n", ext2fs_le32_to_cpu(hdr->f_compat));
138         printf("incompat:\t0x%x\n", ext2fs_le32_to_cpu(hdr->f_incompat));
139         printf("rocompat:\t0x%x\n", ext2fs_le32_to_cpu(hdr->f_rocompat));
140         if (e2undo_has_feature_fs_offset(hdr))
141                 printf("fs offset:\t%llu\n", ext2fs_le64_to_cpu(hdr->fs_offset));
142         printf("header crc:\t0x%x\n", ext2fs_le32_to_cpu(hdr->header_crc));
143 }
144
145 static void print_undo_mismatch(struct ext2_super_block *fs_super,
146                                 struct ext2_super_block *undo_super)
147 {
148         printf("%s",
149                _("The file system superblock doesn't match the undo file.\n"));
150         if (memcmp(fs_super->s_uuid, undo_super->s_uuid,
151                    sizeof(fs_super->s_uuid)))
152                 printf("%s", _("UUID does not match.\n"));
153         if (fs_super->s_mtime != undo_super->s_mtime)
154                 printf("%s", _("Last mount time does not match.\n"));
155         if (fs_super->s_wtime != undo_super->s_wtime)
156                 printf("%s", _("Last write time does not match.\n"));
157         if (fs_super->s_kbytes_written != undo_super->s_kbytes_written)
158                 printf("%s", _("Lifetime write counter does not match.\n"));
159 }
160
161 static int check_filesystem(struct undo_context *ctx, io_channel channel)
162 {
163         struct ext2_super_block super, *sb;
164         char *buf;
165         __u32 sb_crc;
166         errcode_t retval;
167
168         io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
169         retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
170         if (retval) {
171                 com_err(prg_name, retval,
172                         "%s", _("while reading filesystem superblock."));
173                 return retval;
174         }
175
176         /*
177          * Compare the FS and the undo file superblock so that we can't apply
178          * e2undo "patches" out of order.
179          */
180         retval = ext2fs_get_mem(ctx->blocksize, &buf);
181         if (retval) {
182                 com_err(prg_name, retval, "%s", _("while allocating memory"));
183                 return retval;
184         }
185         retval = io_channel_read_blk64(ctx->undo_file, ctx->super_block,
186                                        -SUPERBLOCK_SIZE, buf);
187         if (retval) {
188                 com_err(prg_name, retval, "%s", _("while fetching superblock"));
189                 goto out;
190         }
191         sb = (struct ext2_super_block *)buf;
192         sb->s_magic = ~sb->s_magic;
193         if (memcmp(&super, buf, sizeof(super))) {
194                 print_undo_mismatch(&super, (struct ext2_super_block *)buf);
195                 retval = -1;
196                 goto out;
197         }
198         sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf, SUPERBLOCK_SIZE);
199         if (ext2fs_le32_to_cpu(ctx->hdr.sb_crc) != sb_crc) {
200                 fprintf(stderr,
201                         _("Undo file superblock checksum doesn't match.\n"));
202                 retval = -1;
203                 goto out;
204         }
205
206 out:
207         ext2fs_free_mem(&buf);
208         return retval;
209 }
210
211 static int key_compare(const void *a, const void *b)
212 {
213         const struct undo_key_info *ka, *kb;
214
215         ka = a;
216         kb = b;
217         return ka->fsblk - kb->fsblk;
218 }
219
220 static int e2undo_setup_tdb(const char *name, io_manager *io_ptr)
221 {
222         errcode_t retval = 0;
223         const char *tdb_dir;
224         char *tdb_file = NULL;
225         char *dev_name, *tmp_name;
226
227         /* (re)open a specific undo file */
228         if (undo_file && undo_file[0] != 0) {
229                 retval = set_undo_io_backing_manager(*io_ptr);
230                 if (retval)
231                         goto err;
232                 *io_ptr = undo_io_manager;
233                 retval = set_undo_io_backup_file(undo_file);
234                 if (retval)
235                         goto err;
236                 printf(_("Overwriting existing filesystem; this can be undone "
237                          "using the command:\n"
238                          "    e2undo %s %s\n\n"),
239                          undo_file, name);
240                 return retval;
241         }
242
243         /*
244          * Configuration via a conf file would be
245          * nice
246          */
247         tdb_dir = getenv("E2FSPROGS_UNDO_DIR");
248         if (!tdb_dir)
249                 tdb_dir = "/var/lib/e2fsprogs";
250
251         if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) ||
252             access(tdb_dir, W_OK))
253                 return 0;
254
255         tmp_name = strdup(name);
256         if (!tmp_name)
257                 goto errout;
258         dev_name = basename(tmp_name);
259         tdb_file = malloc(strlen(tdb_dir) + 8 + strlen(dev_name) + 7 + 1);
260         if (!tdb_file) {
261                 free(tmp_name);
262                 goto errout;
263         }
264         sprintf(tdb_file, "%s/e2undo-%s.e2undo", tdb_dir, dev_name);
265         free(tmp_name);
266
267         if ((unlink(tdb_file) < 0) && (errno != ENOENT)) {
268                 retval = errno;
269                 com_err(prg_name, retval,
270                         _("while trying to delete %s"), tdb_file);
271                 goto errout;
272         }
273
274         retval = set_undo_io_backing_manager(*io_ptr);
275         if (retval)
276                 goto errout;
277         *io_ptr = undo_io_manager;
278         retval = set_undo_io_backup_file(tdb_file);
279         if (retval)
280                 goto errout;
281         printf(_("Overwriting existing filesystem; this can be undone "
282                  "using the command:\n"
283                  "    e2undo %s %s\n\n"),
284                  tdb_file, name);
285
286         free(tdb_file);
287         return 0;
288 errout:
289         free(tdb_file);
290 err:
291         com_err(prg_name, retval, "while trying to setup undo file\n");
292         return retval;
293 }
294
295 int main(int argc, char *argv[])
296 {
297         int c, force = 0, dry_run = 0, verbose = 0, dump = 0;
298         io_channel channel;
299         errcode_t retval;
300         int mount_flags, csum_error = 0, io_error = 0;
301         size_t i, keys_per_block;
302         char *device_name, *tdb_file;
303         io_manager manager = unix_io_manager;
304         struct undo_context undo_ctx;
305         char *buf;
306         struct undo_key_block *keyb;
307         struct undo_key *dkey;
308         struct undo_key_info *ikey;
309         __u32 key_crc, blk_crc, hdr_crc;
310         blk64_t lblk;
311         ext2_filsys fs;
312         __u64 offset = 0;
313         char opt_offset_string[40] = { 0 };
314
315 #ifdef ENABLE_NLS
316         setlocale(LC_MESSAGES, "");
317         setlocale(LC_CTYPE, "");
318         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
319         textdomain(NLS_CAT_NAME);
320         set_com_err_gettext(gettext);
321 #endif
322         add_error_table(&et_ext2_error_table);
323
324         prg_name = argv[0];
325         while ((c = getopt(argc, argv, "fhno:vz:")) != EOF) {
326                 switch (c) {
327                 case 'f':
328                         force = 1;
329                         break;
330                 case 'h':
331                         dump = 1;
332                         break;
333                 case 'n':
334                         dry_run = 1;
335                         break;
336                 case 'o':
337                         offset = strtoull(optarg, &buf, 0);
338                         if (*buf) {
339                                 com_err(prg_name, 0,
340                                                 _("illegal offset - %s"), optarg);
341                                 exit(1);
342                         }
343                         /* used to indicate that an offset was specified */
344                         opt_offset_string[0] = 1;
345                         break;
346                 case 'v':
347                         verbose = 1;
348                         break;
349                 case 'z':
350                         undo_file = optarg;
351                         break;
352                 default:
353                         usage();
354                 }
355         }
356
357         if (argc != optind + 2)
358                 usage();
359
360         tdb_file = argv[optind];
361         device_name = argv[optind+1];
362
363         if (undo_file && strcmp(tdb_file, undo_file) == 0) {
364                 printf(_("Will not write to an undo file while replaying it.\n"));
365                 exit(1);
366         }
367
368         /* Interpret the undo file */
369         retval = manager->open(tdb_file, IO_FLAG_EXCLUSIVE,
370                                &undo_ctx.undo_file);
371         if (retval) {
372                 com_err(prg_name, errno,
373                                 _("while opening undo file `%s'\n"), tdb_file);
374                 exit(1);
375         }
376         retval = io_channel_read_blk64(undo_ctx.undo_file, 0,
377                                        -(int)sizeof(undo_ctx.hdr),
378                                        &undo_ctx.hdr);
379         if (retval) {
380                 com_err(prg_name, retval, _("while reading undo file"));
381                 exit(1);
382         }
383         if (memcmp(undo_ctx.hdr.magic, E2UNDO_MAGIC,
384                     sizeof(undo_ctx.hdr.magic))) {
385                 fprintf(stderr, _("%s: Not an undo file.\n"), tdb_file);
386                 exit(1);
387         }
388         if (dump) {
389                 dump_header(&undo_ctx.hdr);
390                 exit(1);
391         }
392         hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&undo_ctx.hdr,
393                                    sizeof(struct undo_header) -
394                                    sizeof(__u32));
395         if (!force && ext2fs_le32_to_cpu(undo_ctx.hdr.header_crc) != hdr_crc) {
396                 fprintf(stderr, _("%s: Header checksum doesn't match.\n"),
397                         tdb_file);
398                 exit(1);
399         }
400         undo_ctx.blocksize = ext2fs_le32_to_cpu(undo_ctx.hdr.block_size);
401         undo_ctx.fs_blocksize = ext2fs_le32_to_cpu(undo_ctx.hdr.fs_block_size);
402         if (undo_ctx.blocksize == 0 || undo_ctx.fs_blocksize == 0) {
403                 fprintf(stderr, _("%s: Corrupt undo file header.\n"), tdb_file);
404                 exit(1);
405         }
406         if (!force && undo_ctx.blocksize > E2UNDO_MAX_BLOCK_SIZE) {
407                 fprintf(stderr, _("%s: Undo block size too large.\n"),
408                         tdb_file);
409                 exit(1);
410         }
411         if (!force && undo_ctx.blocksize < E2UNDO_MIN_BLOCK_SIZE) {
412                 fprintf(stderr, _("%s: Undo block size too small.\n"),
413                         tdb_file);
414                 exit(1);
415         }
416         undo_ctx.super_block = ext2fs_le64_to_cpu(undo_ctx.hdr.super_offset);
417         undo_ctx.num_keys = ext2fs_le64_to_cpu(undo_ctx.hdr.num_keys);
418         io_channel_set_blksize(undo_ctx.undo_file, undo_ctx.blocksize);
419         /*
420          * Do not compare undo_ctx.hdr.f_compat with the available compatible
421          * features set, because a "missing" compatible feature should
422          * not cause any problems.
423          */
424         if (!force && (undo_ctx.hdr.f_incompat || undo_ctx.hdr.f_rocompat)) {
425                 fprintf(stderr, _("%s: Unknown undo file feature set.\n"),
426                         tdb_file);
427                 exit(1);
428         }
429
430         /* open the fs */
431         retval = ext2fs_check_if_mounted(device_name, &mount_flags);
432         if (retval) {
433                 com_err(prg_name, retval, _("Error while determining whether "
434                                 "%s is mounted."), device_name);
435                 exit(1);
436         }
437
438         if (mount_flags & EXT2_MF_MOUNTED) {
439                 com_err(prg_name, retval, "%s", _("e2undo should only be run "
440                                                 "on unmounted filesystems"));
441                 exit(1);
442         }
443
444         if (undo_file) {
445                 retval = e2undo_setup_tdb(device_name, &manager);
446                 if (retval)
447                         exit(1);
448         }
449
450         retval = manager->open(device_name,
451                                IO_FLAG_EXCLUSIVE | (dry_run ? 0 : IO_FLAG_RW),
452                                &channel);
453         if (retval) {
454                 com_err(prg_name, retval,
455                                 _("while opening `%s'"), device_name);
456                 exit(1);
457         }
458
459         if (*opt_offset_string || e2undo_has_feature_fs_offset(&undo_ctx.hdr)) {
460                 if (!*opt_offset_string)
461                         offset = ext2fs_le64_to_cpu(undo_ctx.hdr.fs_offset);
462                 retval = snprintf(opt_offset_string, sizeof(opt_offset_string),
463                                                   "offset=%llu", offset);
464                 if ((size_t) retval >= sizeof(opt_offset_string)) {
465                         /* should not happen... */
466                         com_err(prg_name, 0, _("specified offset is too large"));
467                         exit(1);
468                 }
469                 io_channel_set_options(channel, opt_offset_string);
470         }
471
472         if (!force && check_filesystem(&undo_ctx, channel))
473                 exit(1);
474
475         /* prepare to read keys */
476         retval = ext2fs_get_mem(sizeof(struct undo_key_info) * undo_ctx.num_keys,
477                                 &undo_ctx.keys);
478         if (retval) {
479                 com_err(prg_name, retval, "%s", _("while allocating memory"));
480                 exit(1);
481         }
482         ikey = undo_ctx.keys;
483         retval = ext2fs_get_mem(undo_ctx.blocksize, &keyb);
484         if (retval) {
485                 com_err(prg_name, retval, "%s", _("while allocating memory"));
486                 exit(1);
487         }
488         retval = ext2fs_get_mem(E2UNDO_MAX_EXTENT_BLOCKS * undo_ctx.blocksize,
489                                 &buf);
490         if (retval) {
491                 com_err(prg_name, retval, "%s", _("while allocating memory"));
492                 exit(1);
493         }
494
495         /* load keys */
496         keys_per_block = KEYS_PER_BLOCK(&undo_ctx);
497         lblk = ext2fs_le64_to_cpu(undo_ctx.hdr.key_offset);
498         dbg_printf("nr_keys=%lu, kpb=%zu, blksz=%u\n",
499                    undo_ctx.num_keys, keys_per_block, undo_ctx.blocksize);
500         for (i = 0; i < undo_ctx.num_keys; i += keys_per_block) {
501                 size_t j, max_j;
502                 __le32 crc;
503
504                 retval = io_channel_read_blk64(undo_ctx.undo_file,
505                                                lblk, 1, keyb);
506                 if (retval) {
507                         com_err(prg_name, retval, "%s", _("while reading keys"));
508                         if (force) {
509                                 io_error = 1;
510                                 undo_ctx.num_keys = i - 1;
511                                 break;
512                         }
513                         exit(1);
514                 }
515
516                 /* check keys */
517                 if (!force &&
518                     ext2fs_le32_to_cpu(keyb->magic) != KEYBLOCK_MAGIC) {
519                         fprintf(stderr, _("%s: wrong key magic at %llu\n"),
520                                 tdb_file, lblk);
521                         exit(1);
522                 }
523                 crc = keyb->crc;
524                 keyb->crc = 0;
525                 key_crc = ext2fs_crc32c_le(~0, (unsigned char *)keyb,
526                                            undo_ctx.blocksize);
527                 if (!force && ext2fs_le32_to_cpu(crc) != key_crc) {
528                         fprintf(stderr,
529                                 _("%s: key block checksum error at %llu.\n"),
530                                 tdb_file, lblk);
531                         exit(1);
532                 }
533
534                 /* load keys from key block */
535                 lblk++;
536                 max_j = undo_ctx.num_keys - i;
537                 if (max_j > keys_per_block)
538                         max_j = keys_per_block;
539                 for (j = 0, dkey = keyb->keys;
540                      j < max_j;
541                      j++, ikey++, dkey++) {
542                         ikey->fsblk = ext2fs_le64_to_cpu(dkey->fsblk);
543                         ikey->fileblk = lblk;
544                         ikey->blk_crc = ext2fs_le32_to_cpu(dkey->blk_crc);
545                         ikey->size = ext2fs_le32_to_cpu(dkey->size);
546                         lblk += (ikey->size + undo_ctx.blocksize - 1) /
547                                 undo_ctx.blocksize;
548
549                         if (E2UNDO_MAX_EXTENT_BLOCKS * undo_ctx.blocksize <
550                             ikey->size) {
551                                 com_err(prg_name, retval,
552                                         _("%s: block %llu is too long."),
553                                         tdb_file, ikey->fsblk);
554                                 exit(1);
555                         }
556
557                         /* check each block's crc */
558                         retval = io_channel_read_blk64(undo_ctx.undo_file,
559                                                        ikey->fileblk,
560                                                        -(int)ikey->size,
561                                                        buf);
562                         if (retval) {
563                                 com_err(prg_name, retval,
564                                         _("while fetching block %llu."),
565                                         ikey->fileblk);
566                                 if (!force)
567                                         exit(1);
568                                 io_error = 1;
569                                 continue;
570                         }
571
572                         blk_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf,
573                                                    ikey->size);
574                         if (blk_crc != ikey->blk_crc) {
575                                 fprintf(stderr,
576                                         _("checksum error in filesystem block "
577                                           "%llu (undo blk %llu)\n"),
578                                         ikey->fsblk, ikey->fileblk);
579                                 if (!force)
580                                         exit(1);
581                                 csum_error = 1;
582                         }
583                 }
584         }
585         ext2fs_free_mem(&keyb);
586
587         /* sort keys in fs block order */
588         qsort(undo_ctx.keys, undo_ctx.num_keys, sizeof(struct undo_key_info),
589               key_compare);
590
591         /* replay */
592         io_channel_set_blksize(channel, undo_ctx.fs_blocksize);
593         for (i = 0, ikey = undo_ctx.keys; i < undo_ctx.num_keys; i++, ikey++) {
594                 retval = io_channel_read_blk64(undo_ctx.undo_file,
595                                                ikey->fileblk,
596                                                -(int)ikey->size,
597                                                buf);
598                 if (retval) {
599                         com_err(prg_name, retval,
600                                 _("while fetching block %llu."),
601                                 ikey->fileblk);
602                         io_error = 1;
603                         continue;
604                 }
605
606                 if (verbose)
607                         printf("Replayed block of size %u from %llu to %llu\n",
608                                 ikey->size, ikey->fileblk, ikey->fsblk);
609                 if (dry_run)
610                         continue;
611                 retval = io_channel_write_blk64(channel, ikey->fsblk,
612                                                 -(int)ikey->size, buf);
613                 if (retval) {
614                         com_err(prg_name, retval,
615                                 _("while writing block %llu."), ikey->fsblk);
616                         io_error = 1;
617                 }
618         }
619
620         if (csum_error)
621                 fprintf(stderr, _("Undo file corruption; run e2fsck NOW!\n"));
622         if (io_error)
623                 fprintf(stderr, _("IO error during replay; run e2fsck NOW!\n"));
624         if (!(ext2fs_le32_to_cpu(undo_ctx.hdr.state) & E2UNDO_STATE_FINISHED)) {
625                 force = 1;
626                 fprintf(stderr, _("Incomplete undo record; run e2fsck.\n"));
627         }
628         ext2fs_free_mem(&buf);
629         ext2fs_free_mem(&undo_ctx.keys);
630         io_channel_close(channel);
631
632         /* If there were problems, try to force a fsck */
633         if (!dry_run && (force || csum_error || io_error)) {
634                 retval = ext2fs_open2(device_name, NULL,
635                                    EXT2_FLAG_RW | EXT2_FLAG_64BITS, 0, 0,
636                                    manager, &fs);
637                 if (retval)
638                         goto out;
639                 fs->super->s_state &= ~EXT2_VALID_FS;
640                 if (csum_error || io_error)
641                         fs->super->s_state |= EXT2_ERROR_FS;
642                 ext2fs_mark_super_dirty(fs);
643                 ext2fs_close_free(&fs);
644         }
645
646 out:
647         io_channel_close(undo_ctx.undo_file);
648
649         return csum_error;
650 }