Whamcloud - gitweb
.del-jfs.h~fa6593e8, ChangeLog, jfs_compat.h, journal.c:
[tools/e2fsprogs.git] / e2fsck / journal.c
1 /*
2  * journal.c --- code for handling the "ext3" journal
3  *
4  * Copyright (C) 2000 Andreas Dilger
5  * Copyright (C) 2000 Theodore Ts'o
6  *
7  * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
8  * Copyright (C) 1999 Red Hat Software
9  *
10  * This file may be redistributed under the terms of the
11  * GNU General Public License version 2 or at your discretion
12  * any later version.
13  */
14
15 #ifdef HAVE_SYS_MOUNT_H
16 #include <sys/mount.h>
17 #define MNT_FL (MS_MGC_VAL | MS_RDONLY)
18 #endif
19 #ifdef HAVE_SYS_STAT_H
20 #include <sys/stat.h>
21 #endif
22
23 #include "jfs.h"
24 #include "problem.h"
25 #include "uuid/uuid.h"
26
27 #ifdef JFS_DEBUG
28 static int bh_count = 0;
29 int journal_enable_debug = 2;
30 #endif
31
32 int bmap(struct inode *inode, int block)
33 {
34         int retval;
35         blk_t phys;
36
37         retval = ext2fs_bmap(inode->i_ctx->fs, inode->i_ino, &inode->i_ext2,
38                              NULL, 0, block, &phys);
39
40         if (retval)
41                 com_err(inode->i_ctx->device_name, retval,
42                         _("bmap journal inode %ld, block %d\n"),
43                         inode->i_ino, block);
44
45         return phys;
46 }
47
48 struct buffer_head *getblk(e2fsck_t ctx, blk_t blocknr, int blocksize)
49 {
50         struct buffer_head *bh;
51
52         bh = e2fsck_allocate_memory(ctx, sizeof(*bh), "block buffer");
53         if (!bh)
54                 return NULL;
55
56         jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n",
57                   blocknr, blocksize, ++bh_count);
58
59         bh->b_ctx = ctx;
60         bh->b_size = blocksize;
61         bh->b_blocknr = blocknr;
62
63         return bh;
64 }
65
66 void ll_rw_block(int rw, int dummy, struct buffer_head *bh)
67 {
68         int retval;
69
70         if (rw == READ && !bh->b_uptodate) {
71                 jfs_debug(3, "reading block %lu/%p\n", bh->b_blocknr, bh);
72                 retval = io_channel_read_blk(bh->b_ctx->fs->io, bh->b_blocknr,
73                                              1, bh->b_data);
74                 if (retval) {
75                         com_err(bh->b_ctx->device_name, retval,
76                                 "while reading block %ld\n", bh->b_blocknr);
77                         bh->b_err = retval;
78                         return;
79                 }
80                 bh->b_uptodate = 1;
81         } else if (rw == WRITE && bh->b_dirty) {
82                 jfs_debug(3, "writing block %lu/%p\n", bh->b_blocknr, bh);
83                 retval = io_channel_write_blk(bh->b_ctx->fs->io, bh->b_blocknr,
84                                               1, bh->b_data);
85                 if (retval) {
86                         com_err(bh->b_ctx->device_name, retval,
87                                 "while writing block %ld\n", bh->b_blocknr);
88                         bh->b_err = retval;
89                         return;
90                 }
91                 bh->b_dirty = 0;
92                 bh->b_uptodate = 1;
93         } else
94                 jfs_debug(3, "no-op %s for block %lu\n",
95                           rw == READ ? "read" : "write", bh->b_blocknr);
96 }
97
98 void mark_buffer_dirty(struct buffer_head *bh, int dummy)
99 {
100         bh->b_dirty = dummy | 1; /* use dummy to avoid unused variable */
101 }
102
103 void brelse(struct buffer_head *bh)
104 {
105         if (bh->b_dirty)
106                 ll_rw_block(WRITE, 1, bh);
107         jfs_debug(3, "freeing block %lu/%p (total %d)\n",
108                   bh->b_blocknr, bh, --bh_count);
109         ext2fs_free_mem((void **) &bh);
110 }
111
112 int buffer_uptodate(struct buffer_head *bh)
113 {
114         return bh->b_uptodate;
115 }
116
117 void wait_on_buffer(struct buffer_head *bh)
118 {
119         if (!bh->b_uptodate)
120                 ll_rw_block(READ, 1, bh);
121 }
122
123
124 static void e2fsck_clear_recover(e2fsck_t ctx, int error)
125 {
126         struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
127
128         s->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
129
130         /* if we had an error doing journal recovery, we need a full fsck */
131         if (error)
132                 s->s_state &= ~EXT2_VALID_FS;
133         ext2fs_mark_super_dirty(ctx->fs);
134 }
135
136 static int e2fsck_journal_init_inode(e2fsck_t ctx, struct ext2fs_sb *s,
137                                      ino_t journal_inum, journal_t **journal)
138 {
139         struct inode *inode;
140         const char *cmdname = ctx->program_name;
141         struct buffer_head *bh;
142         blk_t start;
143         int retval;
144
145         jfs_debug(1, "Using journal inode %lu\n", journal_inum);
146         *journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
147         if (!*journal) {
148                 return EXT2_ET_NO_MEMORY;
149         }
150
151         inode = e2fsck_allocate_memory(ctx, sizeof(*inode), "journal inode");
152         if (!inode) {
153                 retval = EXT2_ET_NO_MEMORY;
154                 goto exit_journal;
155         }
156
157         inode->i_ctx = ctx;
158         inode->i_ino = journal_inum;
159         retval = ext2fs_read_inode(ctx->fs, journal_inum, &inode->i_ext2);
160         if (retval)
161                 goto exit_inode;
162
163         (*journal)->j_dev = ctx;
164         (*journal)->j_inode = inode;
165         (*journal)->j_blocksize = ctx->fs->blocksize;
166         (*journal)->j_maxlen = inode->i_ext2.i_size / (*journal)->j_blocksize;
167
168         if (!inode->i_ext2.i_links_count ||
169             !LINUX_S_ISREG(inode->i_ext2.i_mode) ||
170             (*journal)->j_maxlen < JFS_MIN_JOURNAL_BLOCKS ||
171             (start = bmap(inode, 0)) == 0) {
172                 retval = EXT2_ET_BAD_INODE_NUM;
173                 goto exit_inode;
174         }
175
176         bh = getblk(ctx, start, (*journal)->j_blocksize);
177         if (!bh) {
178                 retval = EXT2_ET_NO_MEMORY;
179                 goto exit_inode;
180         }
181         (*journal)->j_sb_buffer = bh;
182         (*journal)->j_superblock = (journal_superblock_t *)bh->b_data;
183
184         return 0;
185
186 exit_inode:
187         ext2fs_free_mem((void **)&inode);
188 exit_journal:
189         ext2fs_free_mem((void **)journal);
190
191         return retval;
192 }
193
194 static int e2fsck_get_journal(e2fsck_t ctx, journal_t **journal)
195 {
196         char uuid_str[40];
197         struct problem_context pctx;
198         struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
199         int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
200
201         clear_problem_context(&pctx);
202
203         if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
204                 if (s->s_journal_dev) {
205                         pctx.num = s->s_journal_dev;
206                         /* this problem aborts on -y, -p, unsupported on -n */
207                         if (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_DEV, &pctx))
208                                 return EXT2_ET_UNSUPP_FEATURE;
209                         s->s_journal_dev = 0;
210                         s->s_state &= ~EXT2_VALID_FS;
211                         ext2fs_mark_super_dirty(ctx->fs);
212                 }
213                 if (!uuid_is_null(s->s_journal_uuid)) {
214                         uuid_unparse(s->s_journal_uuid, uuid_str);
215                         pctx.str = uuid_str;
216                         /* this problem aborts on -y, -p, unsupported on -n */
217                         if (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_UUID, &pctx))
218                                 return EXT2_ET_UNSUPP_FEATURE;
219                         uuid_clear(s->s_journal_uuid);
220                         s->s_state &= ~EXT2_VALID_FS;
221                         ext2fs_mark_super_dirty(ctx->fs);
222                 }
223                 if (!s->s_journal_inum)
224                         return EXT2_ET_BAD_INODE_NUM;
225         }
226
227         if (s->s_journal_dev) {
228                 pctx.num = s->s_journal_dev;
229                 if (!fix_problem(ctx, PR_0_JOURNAL_BAD_DEV, &pctx))
230                         return EXT2_ET_UNSUPP_FEATURE;
231                 s->s_journal_dev = 0;
232                 s->s_state &= ~EXT2_VALID_FS;
233                 ext2fs_mark_super_dirty(ctx->fs);
234         }
235         if (!uuid_is_null(s->s_journal_uuid)) {
236                 uuid_unparse(s->s_journal_uuid, uuid_str);
237                 pctx.str = uuid_str;
238                 if (!fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx))
239                         return EXT2_ET_UNSUPP_FEATURE;
240                 uuid_clear(s->s_journal_uuid);
241                 s->s_state &= ~EXT2_VALID_FS;
242                 ext2fs_mark_super_dirty(ctx->fs);
243         }
244
245         return e2fsck_journal_init_inode(ctx, s, s->s_journal_inum, journal);
246 }
247
248 static int e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
249                                         struct problem_context *pctx)
250 {
251         struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
252         int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
253         int has_journal = s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL;
254
255         if (has_journal || s->s_journal_inum) {
256                 /* The journal inode is bogus, remove and force full fsck */
257                 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
258                         struct ext2fs_sb *s =(struct ext2fs_sb *)ctx->fs->super;
259
260                         if (has_journal && s->s_journal_inum)
261                                 printf("*** ext3 journal has been deleted - "
262                                        "filesystem is now ext2 only ***\n\n");
263                         s->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
264                         s->s_journal_inum = 0;
265                         e2fsck_clear_recover(ctx, 1);
266                         return 0;
267                 }
268                 return EXT2_ET_BAD_INODE_NUM;
269         } else if (recover) {
270                 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
271                         e2fsck_clear_recover(ctx, 1);
272                         return 0;
273                 }
274                 return EXT2_ET_UNSUPP_FEATURE;
275         }
276         return 0;
277 }
278
279 static int e2fsck_journal_fix_unsupported_super(e2fsck_t ctx,
280                                                 struct problem_context *pctx)
281 {
282         struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
283
284         /* Unsupported journal superblock - first choice is abort.
285          * Declining that gives the option to reset the superblock.
286          *
287          * Otherwise we get the chance to delete the journal, and
288          * failing that we abort because we can't handle this.
289          */
290         if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
291             fix_problem(ctx, PR_0_JOURNAL_UNSUPP_SUPER, pctx))
292                 return EXT2_ET_CORRUPT_SUPERBLOCK;
293
294         if (e2fsck_journal_fix_bad_inode(ctx, pctx))
295                 return EXT2_ET_UNSUPP_FEATURE;
296
297         return 0;
298 }
299
300 static int e2fsck_journal_load(journal_t *journal)
301 {
302         e2fsck_t ctx = journal->j_dev;
303         journal_superblock_t *jsb;
304         struct buffer_head *jbh = journal->j_sb_buffer;
305         struct problem_context pctx;
306
307         clear_problem_context(&pctx);
308
309         ll_rw_block(READ, 1, jbh);
310         if (jbh->b_err) {
311                 com_err(ctx->device_name, jbh->b_err,
312                         _("reading journal superblock\n"));
313                 return jbh->b_err;
314         }
315
316         jsb = journal->j_superblock;
317         /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
318         if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
319                 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
320
321         if (jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1) ||
322             jsb->s_blocksize != htonl(journal->j_blocksize)) {
323                 com_err(ctx->device_name, EXT2_ET_CORRUPT_SUPERBLOCK,
324                         _("%s: no valid journal superblock found\n"));
325                 return EXT2_ET_CORRUPT_SUPERBLOCK;
326         }
327
328         if (jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
329                 pctx.num = ntohl(jsb->s_header.h_blocktype);
330                 return e2fsck_journal_fix_unsupported_super(ctx, &pctx);
331         }
332
333         if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
334                 journal->j_maxlen = ntohl(jsb->s_maxlen);
335         else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
336                 com_err(ctx->device_name, EXT2_ET_CORRUPT_SUPERBLOCK,
337                         _("%s: journal too short\n"));
338                 return EXT2_ET_CORRUPT_SUPERBLOCK;
339         }
340
341         journal->j_tail_sequence = ntohl(jsb->s_sequence);
342         journal->j_transaction_sequence = journal->j_tail_sequence;
343         journal->j_tail = ntohl(jsb->s_start);
344         journal->j_first = ntohl(jsb->s_first);
345         journal->j_last = ntohl(jsb->s_maxlen);
346
347         return 0;
348 }
349
350 void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
351                                      blk_t size)
352 {
353         jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
354         jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1);
355         jsb->s_blocksize = htonl(ctx->fs->blocksize);
356         jsb->s_maxlen = htonl(size);
357         jsb->s_first = 1;
358         jsb->s_sequence = htonl(1);
359 }
360
361 static int e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, journal_t *journal,
362                                             struct problem_context *pctx)
363 {
364         struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
365         int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
366
367         pctx->num = journal->j_inode->i_ino;
368
369         if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
370                 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
371                         journal_superblock_t *jsb = journal->j_superblock;
372
373                         e2fsck_journal_reset_super(ctx, jsb, journal->j_maxlen);
374
375                         journal->j_transaction_sequence = 1;
376                         e2fsck_clear_recover(ctx, recover);
377                         return 0;
378                 }
379                 return EXT2_ET_CORRUPT_SUPERBLOCK;
380         } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
381                 return EXT2_ET_CORRUPT_SUPERBLOCK;
382
383         return 0;
384 }
385
386 static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal, int reset)
387 {
388         journal_superblock_t *jsb;
389
390         if (!(ctx->options & E2F_OPT_READONLY)) {
391                 jsb = journal->j_superblock;
392                 jsb->s_sequence = htonl(journal->j_transaction_sequence);
393                 if (reset)
394                         jsb->s_start = 0; /* this marks the journal as empty */
395                 mark_buffer_dirty(journal->j_sb_buffer, 1);
396         }
397         brelse(journal->j_sb_buffer);
398
399         if (journal->j_inode)
400                 free(journal->j_inode);
401         ext2fs_free_mem((void **)&journal);
402 }
403
404 int e2fsck_check_ext3_journal(e2fsck_t ctx)
405 {
406         struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
407         journal_t *journal;
408         int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
409         struct problem_context pctx;
410         int reset = 0;
411         int retval;
412
413         /* If we don't have any journal features, don't do anything more */
414         if (!(s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
415             !recover && s->s_journal_inum == 0 && s->s_journal_dev == 0 &&
416             uuid_is_null(s->s_journal_uuid))
417                 return 0;
418
419         clear_problem_context(&pctx);
420         pctx.num = s->s_journal_inum;
421
422         retval = e2fsck_get_journal(ctx, &journal);
423         if (retval) {
424                 if (retval == EXT2_ET_BAD_INODE_NUM)
425                         return e2fsck_journal_fix_bad_inode(ctx, &pctx);
426                 return retval;
427         }
428
429         retval = e2fsck_journal_load(journal);
430         if (retval) {
431                 if (retval == EXT2_ET_CORRUPT_SUPERBLOCK)
432                         return e2fsck_journal_fix_corrupt_super(ctx, journal,
433                                                                 &pctx);
434                 return retval;
435         }
436
437         /*
438          * We want to make the flags consistent here.  We will not leave with
439          * needs_recovery set but has_journal clear.  We can't get in a loop
440          * with -y, -n, or -p, only if a user isn't making up their mind.
441          */
442 no_has_journal:
443         if (!(s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
444                 recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
445                 pctx.str = "inode";
446                 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
447                         if (recover &&
448                             !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
449                                 goto no_has_journal;
450                         s->s_journal_inum = 0;
451                         e2fsck_clear_recover(ctx, recover);
452                 } else if (!(ctx->options & E2F_OPT_READONLY)) {
453                         s->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
454                         ext2fs_mark_super_dirty(ctx->fs);
455                 }
456         }
457
458         if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
459             !(s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
460             journal->j_superblock->s_start != 0) {
461                 if (fix_problem(ctx, PR_0_JOURNAL_RESET_JOURNAL, &pctx))
462                         reset = 1;
463                 /* I refuse to enable recovery for journal */
464         }
465
466         e2fsck_journal_release(ctx, journal, reset);
467         return retval;
468 }
469
470 static int recover_ext3_journal(e2fsck_t ctx)
471 {
472         ext2_filsys fs = ctx->fs;
473         journal_t *journal;
474         int retval;
475
476         retval = e2fsck_get_journal(ctx, &journal);
477         if (retval)
478                 return retval;
479
480         retval = e2fsck_journal_load(journal);
481         if (retval)
482                 return retval;
483
484         retval = -journal_recover(journal);
485         e2fsck_journal_release(ctx, journal, 1);
486         return retval;
487 }
488
489
490 #if 0
491 #define TEMPLATE "/tmp/ext3.XXXXXX"
492
493 /*
494  * This function attempts to mount and unmount an ext3 filesystem,
495  * which is a cheap way to force the kernel to run the journal and
496  * handle the recovery for us.
497  */
498 static int recover_ext3_journal_via_mount(e2fsck_t ctx)
499 {
500         ext2_filsys fs = ctx->fs;
501         char    *dirlist[] = {"/mnt","/lost+found","/tmp","/root","/boot",0};
502         errcode_t        retval, retval2;
503         int      count = 0;
504         char     template[] = TEMPLATE;
505         struct stat buf;
506         char    *tmpdir;
507
508         if (ctx->options & E2F_OPT_READONLY) {
509                 printf("%s: won't do journal recovery while read-only\n",
510                        ctx->device_name);
511                 return EXT2_ET_FILE_RO;
512         }
513
514         printf(_("%s: trying for ext3 kernel journal recovery\n"),
515                ctx->device_name);
516         /*
517          * First try to make a temporary directory.  This may fail if
518          * the root partition is still mounted read-only.
519          */
520 newtemp:
521         tmpdir = mktemp(template);
522         if (tmpdir) {
523                 jfs_debug(2, "trying %s as ext3 temp mount point\n", tmpdir);
524                 if (mkdir(template, 0700)) {
525                         if (errno == EROFS) {
526                                 tmpdir = NULL;
527                                 template[0] = '\0';
528                         } else if (errno == EEXIST && count++ < 10) {
529                                 strcpy(template, TEMPLATE);
530                                 goto newtemp;
531                         }
532                         return errno;
533                 }
534         }
535
536         /*
537          * OK, creating a temporary directory didn't work.
538          * Let's try a list of possible temporary mountpoints.
539          */
540         if (!tmpdir) {
541                 dev_t   rootdev;
542                 char    **cpp, *dir;
543
544                 if (stat("/", &buf))
545                         return errno;
546
547                 rootdev = buf.st_dev;
548
549                 /*
550                  * Check that dir is on the same device as root (no other
551                  * filesystem is mounted there), and it's a directory.
552                  */
553                 for (cpp = dirlist; (dir = *cpp); cpp++)
554                         if (stat(dir, &buf) == 0 && buf.st_dev == rootdev &&
555                             S_ISDIR(buf.st_mode)) {
556                                 tmpdir = dir;
557                                 break;
558                         }
559         }
560
561         if (tmpdir) {
562                 io_manager      io_ptr = fs->io->manager;
563                 int             blocksize = fs->blocksize;
564
565                 jfs_debug(2, "using %s for ext3 mount\n", tmpdir);
566                 /* FIXME - need to handle loop devices here */
567                 if (mount(ctx->device_name, tmpdir, "ext3", MNT_FL, NULL)) {
568                         retval = errno;
569                         com_err(ctx->program_name, errno,
570                                 "when mounting %s", ctx->device_name);
571                         if (template[0])
572                                 rmdir(tmpdir);
573                         return retval;
574                 }
575                 /*
576                  * Now that it mounted cleanly, the filesystem will have been
577                  * recovered, so we can now unmount it.
578                  */
579                 if (umount(tmpdir))
580                         return errno;
581
582                 /*
583                  * Remove the temporary directory, if it was created.
584                  */
585                 if (template[0])
586                         rmdir(tmpdir);
587                 return 0;
588         }
589 }
590 #endif
591
592 int e2fsck_run_ext3_journal(e2fsck_t ctx)
593 {
594         io_manager io_ptr = ctx->fs->io->manager;
595         int blocksize = ctx->fs->blocksize;
596         errcode_t       retval, recover_retval;
597
598         printf(_("%s: recovering journal\n"), ctx->device_name);
599         if (ctx->options & E2F_OPT_READONLY) {
600                 printf(_("%s: won't do journal recovery while read-only\n"),
601                        ctx->device_name);
602                 return EXT2_ET_FILE_RO;
603         }
604
605         recover_retval = recover_ext3_journal(ctx);
606         
607         /*
608          * Reload the filesystem context to get up-to-date data from disk
609          * because journal recovery will change the filesystem under us.
610          */
611         ext2fs_close(ctx->fs);
612         retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
613                              ctx->superblock, blocksize, io_ptr,
614                              &ctx->fs);
615
616         if (retval) {
617                 com_err(ctx->program_name, retval,
618                         _("while trying to re-open %s"),
619                         ctx->device_name);
620                 fatal_error(ctx, 0);
621         }
622         ctx->fs->priv_data = ctx;
623
624         /* Set the superblock flags */
625         e2fsck_clear_recover(ctx, recover_retval);
626         return recover_retval;
627 }