Whamcloud - gitweb
e2fsck: optimize loop counter when fixing bitmap padding
[tools/e2fsprogs.git] / e2fsck / journal.c
index 0a53cc7..155857d 100644 (file)
@@ -58,7 +58,7 @@ int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
                return 0;
        }
 
-       retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino, 
+       retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
                            &inode->i_ext2, NULL, 0, block, &pblk);
        *phys = pblk;
        return (retval);
@@ -68,18 +68,24 @@ int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
 struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
 {
        struct buffer_head *bh;
+       int bufsize = sizeof(*bh) + kdev->k_ctx->fs->blocksize -
+               sizeof(bh->b_data);
 
-       bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
+       bh = e2fsck_allocate_memory(kdev->k_ctx, bufsize, "block buffer");
        if (!bh)
                return NULL;
 
+#ifdef CONFIG_JBD_DEBUG
+       if (journal_enable_debug >= 3)
+               bh_count++;
+#endif
        jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n",
-                 (unsigned long) blocknr, blocksize, ++bh_count);
+                 (unsigned long) blocknr, blocksize, bh_count);
 
        bh->b_ctx = kdev->k_ctx;
        if (kdev->k_dev == K_DEV_FS)
                bh->b_io = kdev->k_ctx->fs->io;
-       else 
+       else
                bh->b_io = kdev->k_ctx->journal_io;
        bh->b_size = blocksize;
        bh->b_blocknr = blocknr;
@@ -93,7 +99,7 @@ void sync_blockdev(kdev_t kdev)
 
        if (kdev->k_dev == K_DEV_FS)
                io = kdev->k_ctx->fs->io;
-       else 
+       else
                io = kdev->k_ctx->journal_io;
 
        io_channel_flush(io);
@@ -107,28 +113,28 @@ void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
        for (; nr > 0; --nr) {
                bh = *bhp++;
                if (rw == READ && !bh->b_uptodate) {
-                       jfs_debug(3, "reading block %lu/%p\n", 
+                       jfs_debug(3, "reading block %lu/%p\n",
                                  (unsigned long) bh->b_blocknr, (void *) bh);
-                       retval = io_channel_read_blk(bh->b_io, 
+                       retval = io_channel_read_blk(bh->b_io,
                                                     bh->b_blocknr,
                                                     1, bh->b_data);
                        if (retval) {
                                com_err(bh->b_ctx->device_name, retval,
-                                       "while reading block %lu\n", 
+                                       "while reading block %lu\n",
                                        (unsigned long) bh->b_blocknr);
                                bh->b_err = retval;
                                continue;
                        }
                        bh->b_uptodate = 1;
                } else if (rw == WRITE && bh->b_dirty) {
-                       jfs_debug(3, "writing block %lu/%p\n", 
+                       jfs_debug(3, "writing block %lu/%p\n",
                                  (unsigned long) bh->b_blocknr, (void *) bh);
-                       retval = io_channel_write_blk(bh->b_io, 
+                       retval = io_channel_write_blk(bh->b_io,
                                                      bh->b_blocknr,
                                                      1, bh->b_data);
                        if (retval) {
                                com_err(bh->b_ctx->device_name, retval,
-                                       "while writing block %lu\n", 
+                                       "while writing block %lu\n",
                                        (unsigned long) bh->b_blocknr);
                                bh->b_err = retval;
                                continue;
@@ -137,7 +143,7 @@ void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
                        bh->b_uptodate = 1;
                } else {
                        jfs_debug(3, "no-op %s for block %lu\n",
-                                 rw == READ ? "read" : "write", 
+                                 rw == READ ? "read" : "write",
                                  (unsigned long) bh->b_blocknr);
                }
        }
@@ -189,8 +195,37 @@ static void e2fsck_clear_recover(e2fsck_t ctx, int error)
        ext2fs_mark_super_dirty(ctx->fs);
 }
 
+/*
+ * This is a helper function to check the validity of the journal.
+ */
+struct process_block_struct {
+       e2_blkcnt_t     last_block;
+};
+
+static int process_journal_block(ext2_filsys fs,
+                                blk_t  *block_nr,
+                                e2_blkcnt_t blockcnt,
+                                blk_t ref_block EXT2FS_ATTR((unused)),
+                                int ref_offset EXT2FS_ATTR((unused)),
+                                void *priv_data)
+{
+       struct process_block_struct *p;
+       blk_t   blk = *block_nr;
+
+       p = (struct process_block_struct *) priv_data;
+
+       if (blk < fs->super->s_first_data_block ||
+           blk >= fs->super->s_blocks_count)
+               return BLOCK_ABORT;
+
+       if (blockcnt >= 0)
+               p->last_block = blockcnt;
+       return 0;
+}
+
 static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
 {
+       struct process_block_struct pb;
        struct ext2_super_block *sb = ctx->fs->super;
        struct ext2_super_block jsuper;
        struct problem_context  pctx;
@@ -202,13 +237,11 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
        errcode_t               retval = 0;
        io_manager              io_ptr = 0;
        unsigned long           start = 0;
-       blk_t                   blk;
        int                     ext_journal = 0;
        int                     tried_backup_jnl = 0;
-       int                     i;
-               
+
        clear_problem_context(&pctx);
-       
+
        journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
        if (!journal) {
                return EXT2_ET_NO_MEMORY;
@@ -220,19 +253,21 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                goto errout;
        }
        dev_journal = dev_fs+1;
-       
+
        dev_fs->k_ctx = dev_journal->k_ctx = ctx;
        dev_fs->k_dev = K_DEV_FS;
        dev_journal->k_dev = K_DEV_JOURNAL;
-       
+
        journal->j_dev = dev_journal;
        journal->j_fs_dev = dev_fs;
        journal->j_inode = NULL;
        journal->j_blocksize = ctx->fs->blocksize;
 
        if (uuid_is_null(sb->s_journal_uuid)) {
-               if (!sb->s_journal_inum)
-                       return EXT2_ET_BAD_INODE_NUM;
+               if (!sb->s_journal_inum) {
+                       retval = EXT2_ET_BAD_INODE_NUM;
+                       goto errout;
+               }
                j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
                                                 "journal inode");
                if (!j_inode) {
@@ -242,7 +277,7 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
 
                j_inode->i_ctx = ctx;
                j_inode->i_ino = sb->s_journal_inum;
-               
+
                if ((retval = ext2fs_read_inode(ctx->fs,
                                                sb->s_journal_inum,
                                                &j_inode->i_ext2))) {
@@ -251,11 +286,14 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                            tried_backup_jnl)
                                goto errout;
                        memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
-                       memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, 
+                       memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
                               EXT2_N_BLOCKS*4);
                        j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
                        j_inode->i_ext2.i_links_count = 1;
                        j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
+                       e2fsck_use_inode_shortcuts(ctx, 1);
+                       ctx->stashed_ino = j_inode->i_ino;
+                       ctx->stashed_inode = &j_inode->i_ext2;
                        tried_backup_jnl++;
                }
                if (!j_inode->i_ext2.i_links_count ||
@@ -268,21 +306,22 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                        retval = EXT2_ET_JOURNAL_TOO_SMALL;
                        goto try_backup_journal;
                }
-               for (i=0; i < EXT2_N_BLOCKS; i++) {
-                       blk = j_inode->i_ext2.i_block[i];
-                       if (!blk) {
-                               if (i < EXT2_NDIR_BLOCKS) {
-                                       retval = EXT2_ET_JOURNAL_TOO_SMALL;
-                                       goto try_backup_journal;
-                               }
-                               continue;
-                       }
-                       if (blk < sb->s_first_data_block ||
-                           blk >= sb->s_blocks_count) {
-                               retval = EXT2_ET_BAD_BLOCK_NUM;
-                               goto try_backup_journal;
-                       }
+               pb.last_block = -1;
+               retval = ext2fs_block_iterate2(ctx->fs, j_inode->i_ino,
+                                              BLOCK_FLAG_HOLE, 0,
+                                              process_journal_block, &pb);
+               if ((pb.last_block+1) * ctx->fs->blocksize <
+                   j_inode->i_ext2.i_size) {
+                       retval = EXT2_ET_JOURNAL_TOO_SMALL;
+                       goto try_backup_journal;
+               }
+               if (tried_backup_jnl && !(ctx->options & E2F_OPT_READONLY)) {
+                       retval = ext2fs_write_inode(ctx->fs, sb->s_journal_inum,
+                                                   &j_inode->i_ext2);
+                       if (retval)
+                               goto errout;
                }
+
                journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
 
 #ifdef USE_INODE_IO
@@ -311,12 +350,13 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                                ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
                }
                journal_name = ctx->journal_name;
-                       
+
                if (!journal_name) {
                        fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
-                       return EXT2_ET_LOAD_EXT_JOURNAL;
+                       retval = EXT2_ET_LOAD_EXT_JOURNAL;
+                       goto errout;
                }
-               
+
                jfs_debug(1, "Using journal file %s\n", journal_name);
                io_ptr = unix_io_manager;
        }
@@ -344,13 +384,15 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                        goto errout;
                }
                ll_rw_block(READ, 1, &bh);
-               if ((retval = bh->b_err) != 0)
+               if ((retval = bh->b_err) != 0) {
+                       brelse(bh);
                        goto errout;
+               }
                memcpy(&jsuper, start ? bh->b_data :  bh->b_data + 1024,
                       sizeof(jsuper));
                brelse(bh);
-#ifdef EXT2FS_ENABLE_SWAPFS
-               if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) 
+#ifdef WORDS_BIGENDIAN
+               if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
                        ext2fs_swap_super(&jsuper);
 #endif
                if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
@@ -366,7 +408,7 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                        retval = EXT2_ET_LOAD_EXT_JOURNAL;
                        goto errout;
                }
-               
+
                journal->j_maxlen = jsuper.s_blocks_count;
                start++;
        }
@@ -375,19 +417,21 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
                retval = EXT2_ET_NO_MEMORY;
                goto errout;
        }
-       
+
        journal->j_sb_buffer = bh;
        journal->j_superblock = (journal_superblock_t *)bh->b_data;
-       
+
 #ifdef USE_INODE_IO
        if (j_inode)
                ext2fs_free_mem(&j_inode);
 #endif
 
        *ret_journal = journal;
+       e2fsck_use_inode_shortcuts(ctx, 0);
        return 0;
 
 errout:
+       e2fsck_use_inode_shortcuts(ctx, 0);
        if (dev_fs)
                ext2fs_free_mem(&dev_fs);
        if (j_inode)
@@ -395,7 +439,6 @@ errout:
        if (journal)
                ext2fs_free_mem(&journal);
        return retval;
-       
 }
 
 static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
@@ -416,7 +459,8 @@ static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
                                       "filesystem is now ext2 only ***\n\n");
                        sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
                        sb->s_journal_inum = 0;
-                       ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
+                       ctx->flags |= E2F_FLAG_JOURNAL_INODE;
+                       ctx->fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
                        e2fsck_clear_recover(ctx, 1);
                        return 0;
                }
@@ -478,7 +522,7 @@ static errcode_t e2fsck_journal_load(journal_t *journal)
                    jsb->s_nr_users)
                        clear_v2_journal_fields(journal);
                break;
-               
+
        case JFS_SUPERBLOCK_V2:
                journal->j_format_version = 2;
                if (ntohl(jsb->s_nr_users) > 1 &&
@@ -498,7 +542,7 @@ static errcode_t e2fsck_journal_load(journal_t *journal)
        case JFS_COMMIT_BLOCK:
        case JFS_REVOKE_BLOCK:
                return EXT2_ET_CORRUPT_SUPERBLOCK;
-               
+
        /* If we don't understand the superblock major type, but there
         * is a magic number, then it is likely to be a new format we
         * just don't understand, so leave it alone. */
@@ -508,7 +552,7 @@ static errcode_t e2fsck_journal_load(journal_t *journal)
 
        if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
                return EXT2_ET_UNSUPP_FEATURE;
-       
+
        if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
                return EXT2_ET_RO_UNSUPP_FEATURE;
 
@@ -554,7 +598,7 @@ static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
        /* Leave a valid existing V1 superblock signature alone.
         * Anything unrecognisable we overwrite with a new V2
         * signature. */
-       
+
        if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
            jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
                jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
@@ -562,7 +606,7 @@ static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
        }
 
        /* Zero out everything else beyond the superblock header */
-       
+
        p = ((char *) jsb) + sizeof(journal_header_t);
        memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
 
@@ -628,7 +672,7 @@ static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
                        io_channel_close(ctx->journal_io);
                ctx->journal_io = 0;
        }
-       
+
 #ifndef USE_INODE_IO
        if (journal->j_inode)
                ext2fs_free_mem(&journal->j_inode);
@@ -716,6 +760,7 @@ no_has_journal:
                        e2fsck_clear_recover(ctx, force_fsck);
                } else if (!(ctx->options & E2F_OPT_READONLY)) {
                        sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+                       ctx->fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
                        ext2fs_mark_super_dirty(ctx->fs);
                }
        }
@@ -745,7 +790,7 @@ no_has_journal:
                 * ignore the fact that journal apparently has data;
                 * accidentally replaying over valid data would be far
                 * worse than skipping a questionable recovery.
-                * 
+                *
                 * XXX should we abort with a fatal error here?  What
                 * will the ext3 kernel code do if a filesystem with
                 * !NEEDS_RECOVERY but with a non-zero
@@ -759,9 +804,12 @@ no_has_journal:
 
 static errcode_t recover_ext3_journal(e2fsck_t ctx)
 {
+       struct problem_context  pctx;
        journal_t *journal;
        int retval;
 
+       clear_problem_context(&pctx);
+
        journal_init_revoke_caches();
        retval = e2fsck_get_journal(ctx, &journal);
        if (retval)
@@ -774,18 +822,26 @@ static errcode_t recover_ext3_journal(e2fsck_t ctx)
        retval = journal_init_revoke(journal, 1024);
        if (retval)
                goto errout;
-       
+
        retval = -journal_recover(journal);
        if (retval)
                goto errout;
-       
+
+       if (journal->j_failed_commit) {
+               pctx.ino = journal->j_failed_commit;
+               fix_problem(ctx, PR_0_JNL_TXN_CORRUPT, &pctx);
+               ctx->fs->super->s_state |= EXT2_ERROR_FS;
+               ext2fs_mark_super_dirty(ctx->fs);
+       }
+
+
        if (journal->j_superblock->s_errno) {
                ctx->fs->super->s_state |= EXT2_ERROR_FS;
                ext2fs_mark_super_dirty(ctx->fs);
                journal->j_superblock->s_errno = 0;
                mark_buffer_dirty(journal->j_sb_buffer);
        }
-               
+
 errout:
        journal_destroy_revoke(journal);
        journal_destroy_revoke_caches();
@@ -798,6 +854,8 @@ int e2fsck_run_ext3_journal(e2fsck_t ctx)
        io_manager io_ptr = ctx->fs->io->manager;
        int blocksize = ctx->fs->blocksize;
        errcode_t       retval, recover_retval;
+       io_stats        stats = 0;
+       unsigned long long kbytes_written = 0;
 
        printf(_("%s: recovering journal\n"), ctx->device_name);
        if (ctx->options & E2F_OPT_READONLY) {
@@ -810,16 +868,20 @@ int e2fsck_run_ext3_journal(e2fsck_t ctx)
                ext2fs_flush(ctx->fs);  /* Force out any modifications */
 
        recover_retval = recover_ext3_journal(ctx);
-       
+
        /*
         * Reload the filesystem context to get up-to-date data from disk
         * because journal recovery will change the filesystem under us.
         */
-       ext2fs_close(ctx->fs);
+       if (ctx->fs->super->s_kbytes_written &&
+           ctx->fs->io->manager->get_stats)
+               ctx->fs->io->manager->get_stats(ctx->fs->io, &stats);
+       if (stats && stats->bytes_written)
+               kbytes_written = stats->bytes_written >> 10;
+       ext2fs_free(ctx->fs);
        retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
                             ctx->superblock, blocksize, io_ptr,
                             &ctx->fs);
-
        if (retval) {
                com_err(ctx->program_name, retval,
                        _("while trying to re-open %s"),
@@ -828,6 +890,8 @@ int e2fsck_run_ext3_journal(e2fsck_t ctx)
        }
        ctx->fs->priv_data = ctx;
        ctx->fs->now = ctx->now;
+       ctx->fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+       ctx->fs->super->s_kbytes_written += kbytes_written;
 
        /* Set the superblock flags */
        e2fsck_clear_recover(ctx, recover_retval);
@@ -851,7 +915,7 @@ void e2fsck_move_ext3_journal(e2fsck_t ctx)
        errcode_t               retval;
        const char * const *    cpp;
        int                     group, mount_flags;
-       
+
        clear_problem_context(&pctx);
 
        /*
@@ -890,7 +954,7 @@ void e2fsck_move_ext3_journal(e2fsck_t ctx)
         */
        if (sb->s_journal_inum == EXT2_JOURNAL_INO)
                return;
-       
+
        /*
         * The journal inode had better have only one link and not be readable.
         */
@@ -926,7 +990,7 @@ void e2fsck_move_ext3_journal(e2fsck_t ctx)
        pctx.str = *cpp;
        if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
                return;
-               
+
        /*
         * OK, we've done all the checks, let's actually move the
         * journal inode.  Errors at this point mean we need to force
@@ -948,6 +1012,7 @@ void e2fsck_move_ext3_journal(e2fsck_t ctx)
        ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
        ext2fs_mark_ib_dirty(fs);
        fs->group_desc[group].bg_free_inodes_count++;
+       ext2fs_group_desc_csum_set(fs, group);
        fs->super->s_free_inodes_count++;
        return;
 
@@ -959,3 +1024,38 @@ err_out:
        return;
 }
 
+/*
+ * This function makes sure the superblock hint for the external
+ * journal is correct.
+ */
+int e2fsck_fix_ext3_journal_hint(e2fsck_t ctx)
+{
+       struct ext2_super_block *sb = ctx->fs->super;
+       struct problem_context pctx;
+       char uuid[37], *journal_name;
+       struct stat st;
+
+       if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) ||
+           uuid_is_null(sb->s_journal_uuid))
+               return 0;
+
+       uuid_unparse(sb->s_journal_uuid, uuid);
+       journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid);
+       if (!journal_name)
+               return 0;
+
+       if (stat(journal_name, &st) < 0)
+               return 0;
+
+       if (st.st_rdev != sb->s_journal_dev) {
+               clear_problem_context(&pctx);
+               pctx.num = st.st_rdev;
+               if (fix_problem(ctx, PR_0_EXTERNAL_JOURNAL_HINT, &pctx)) {
+                       sb->s_journal_dev = st.st_rdev;
+                       ext2fs_mark_super_dirty(ctx->fs);
+               }
+       }
+
+       free(journal_name);
+       return 0;
+}