Whamcloud - gitweb
fix unused-function -Wall warnings
[tools/e2fsprogs.git] / e2fsck / recovery.c
index a0e15e8..8ca3527 100644 (file)
@@ -35,7 +35,6 @@ struct recovery_info
        int             nr_revoke_hits;
 };
 
-enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
 static int do_one_pass(journal_t *journal,
                                struct recovery_info *info, enum passtype pass);
 static int scan_revoke_records(journal_t *, struct buffer_head *,
@@ -75,8 +74,8 @@ static int do_readahead(journal_t *journal, unsigned int start)
 
        /* Do up to 128K of readahead */
        max = start + (128 * 1024 / journal->j_blocksize);
-       if (max > journal->j_maxlen)
-               max = journal->j_maxlen;
+       if (max > journal->j_total_len)
+               max = journal->j_total_len;
 
        /* Do the readahead itself.  We'll submit MAXBUF buffer_heads at
         * a time to the block device IO layer. */
@@ -121,27 +120,6 @@ failed:
 
 #endif /* __KERNEL__ */
 
-static inline __u32 get_be32(__be32 *p)
-{
-       unsigned char *cp = (unsigned char *) p;
-       __u32 ret;
-
-       ret = *cp++;
-       ret = (ret << 8) + *cp++;
-       ret = (ret << 8) + *cp++;
-       ret = (ret << 8) + *cp++;
-       return ret;
-}
-
-static inline __u16 get_be16(__be16 *p)
-{
-       unsigned char *cp = (unsigned char *) p;
-       __u16 ret;
-
-       ret = *cp++;
-       ret = (ret << 8) + *cp++;
-       return ret;
-}
 
 /*
  * Read a block from the journal
@@ -156,7 +134,7 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
 
        *bhp = NULL;
 
-       if (offset >= journal->j_maxlen) {
+       if (offset >= journal->j_total_len) {
                printk(KERN_ERR "JBD2: corrupted journal superblock\n");
                return -EFSCORRUPTED;
        }
@@ -192,24 +170,23 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
        return 0;
 }
 
-static int jbd2_descr_block_csum_verify(journal_t *j,
-                                       void *buf)
+static int jbd2_descriptor_block_csum_verify(journal_t *j, void *buf)
 {
        struct jbd2_journal_block_tail *tail;
-       __u32 provided;
+       __be32 provided;
        __u32 calculated;
 
        if (!jbd2_journal_has_csum_v2or3(j))
                return 1;
 
-       tail = (struct jbd2_journal_block_tail *)((char *)buf + j->j_blocksize -
-                       sizeof(struct jbd2_journal_block_tail));
+       tail = (struct jbd2_journal_block_tail *)((char *)buf +
+               j->j_blocksize - sizeof(struct jbd2_journal_block_tail));
        provided = tail->t_checksum;
        tail->t_checksum = 0;
        calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
        tail->t_checksum = provided;
 
-       return provided == ext2fs_cpu_to_be32(calculated);
+       return provided == cpu_to_be32(calculated);
 }
 
 /*
@@ -219,7 +196,7 @@ static int jbd2_descr_block_csum_verify(journal_t *j,
 static int count_tags(journal_t *journal, struct buffer_head *bh)
 {
        char *                  tagp;
-       journal_block_tag_t *   tag;
+       journal_block_tag_t     tag;
        int                     nr = 0, size = journal->j_blocksize;
        int                     tag_bytes = journal_tag_bytes(journal);
 
@@ -229,14 +206,14 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
        tagp = &bh->b_data[sizeof(journal_header_t)];
 
        while ((tagp - bh->b_data + tag_bytes) <= size) {
-               tag = (journal_block_tag_t *) tagp;
+               memcpy(&tag, tagp, sizeof(tag));
 
                nr++;
                tagp += tag_bytes;
-               if (!(get_be16(&tag->t_flags) & JBD2_FLAG_SAME_UUID))
+               if (!(tag.t_flags & cpu_to_be16(JBD2_FLAG_SAME_UUID)))
                        tagp += 16;
 
-               if (get_be16(&tag->t_flags) & JBD2_FLAG_LAST_TAG)
+               if (tag.t_flags & cpu_to_be16(JBD2_FLAG_LAST_TAG))
                        break;
        }
 
@@ -247,10 +224,50 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
 /* Make sure we wrap around the log correctly! */
 #define wrap(journal, var)                                             \
 do {                                                                   \
-       if (var >= (journal)->j_last)                                   \
-               var -= ((journal)->j_last - (journal)->j_first);        \
+       unsigned long _wrap_last =                                      \
+               jbd2_has_feature_fast_commit(journal) ?                 \
+                       (journal)->j_fc_last : (journal)->j_last;       \
+                                                                       \
+       if (var >= _wrap_last)                                          \
+               var -= (_wrap_last - (journal)->j_first);               \
 } while (0)
 
+static int fc_do_one_pass(journal_t *journal,
+                         struct recovery_info *info, enum passtype pass)
+{
+       unsigned int expected_commit_id = info->end_transaction;
+       unsigned long next_fc_block;
+       struct buffer_head *bh;
+       int err = 0;
+
+       next_fc_block = journal->j_fc_first;
+       if (!journal->j_fc_replay_callback)
+               return 0;
+
+       while (next_fc_block <= journal->j_fc_last) {
+               jbd_debug(3, "Fast commit replay: next block %ld\n",
+                         next_fc_block);
+               err = jread(&bh, journal, next_fc_block);
+               if (err) {
+                       jbd_debug(3, "Fast commit replay: read error\n");
+                       break;
+               }
+
+               err = journal->j_fc_replay_callback(journal, bh, pass,
+                                       next_fc_block - journal->j_fc_first,
+                                       expected_commit_id);
+               next_fc_block++;
+               if (err < 0 || err == JBD2_FC_REPLAY_STOP)
+                       break;
+               err = 0;
+       }
+
+       if (err)
+               jbd_debug(3, "Fast commit replay failed, err = %d\n", err);
+
+       return err;
+}
+
 /**
  * jbd2_journal_recover - recovers a on-disk journal
  * @journal: the journal to recover
@@ -281,8 +298,8 @@ int jbd2_journal_recover(journal_t *journal)
 
        if (!sb->s_start) {
                jbd_debug(1, "No recovery required, last transaction %d\n",
-                         ext2fs_be32_to_cpu(sb->s_sequence));
-               journal->j_transaction_sequence = ext2fs_be32_to_cpu(sb->s_sequence) + 1;
+                         be32_to_cpu(sb->s_sequence));
+               journal->j_transaction_sequence = be32_to_cpu(sb->s_sequence) + 1;
                return 0;
        }
 
@@ -308,7 +325,7 @@ int jbd2_journal_recover(journal_t *journal)
                err = err2;
        /* Make sure all replayed data is on permanent storage */
        if (journal->j_flags & JBD2_BARRIER) {
-               err2 = blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL);
+               err2 = blkdev_issue_flush(journal->j_fs_dev);
                if (!err)
                        err = err2;
        }
@@ -343,8 +360,8 @@ int jbd2_journal_skip_recovery(journal_t *journal)
                ++journal->j_transaction_sequence;
        } else {
 #ifdef CONFIG_JBD2_DEBUG
-               int dropped = info.end_transaction -
-                       ext2fs_be32_to_cpu(journal->j_superblock->s_sequence);
+               int dropped = info.end_transaction - 
+                       be32_to_cpu(journal->j_superblock->s_sequence);
                jbd_debug(1,
                          "JBD2: ignoring %d transaction%s from the journal.\n",
                          dropped, (dropped == 1) ? "" : "s");
@@ -359,9 +376,9 @@ int jbd2_journal_skip_recovery(journal_t *journal)
 static inline unsigned long long read_tag_block(journal_t *journal,
                                                journal_block_tag_t *tag)
 {
-       unsigned long long block = get_be32(&tag->t_blocknr);
+       unsigned long long block = be32_to_cpu(tag->t_blocknr);
        if (jbd2_has_feature_64bit(journal))
-               block |= (u64)get_be32(&tag->t_blocknr_high) << 32;
+               block |= (u64)be32_to_cpu(tag->t_blocknr_high) << 32;
        return block;
 }
 
@@ -400,7 +417,7 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,
 static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
 {
        struct commit_header *h;
-       __u32 provided;
+       __be32 provided;
        __u32 calculated;
 
        if (!jbd2_journal_has_csum_v2or3(j))
@@ -412,27 +429,27 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
        calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
        h->h_chksum[0] = provided;
 
-       return provided == ext2fs_cpu_to_be32(calculated);
+       return provided == cpu_to_be32(calculated);
 }
 
 static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
+                                     journal_block_tag3_t *tag3,
                                      void *buf, __u32 sequence)
 {
-       journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag;
        __u32 csum32;
-       __u32 seq;
+       __be32 seq;
 
        if (!jbd2_journal_has_csum_v2or3(j))
                return 1;
 
-       seq = ext2fs_cpu_to_be32(sequence);
+       seq = cpu_to_be32(sequence);
        csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq));
        csum32 = jbd2_chksum(j, csum32, buf, j->j_blocksize);
 
        if (jbd2_has_feature_csum3(j))
-               return get_be32(&tag3->t_checksum) == csum32;
-
-       return get_be16(&tag->t_checksum) == (csum32 & 0xFFFF);
+               return tag3->t_checksum == cpu_to_be32(csum32);
+       else
+               return tag->t_checksum == cpu_to_be16(csum32);
 }
 
 static int do_one_pass(journal_t *journal,
@@ -450,6 +467,8 @@ static int do_one_pass(journal_t *journal,
        __u32                   crc32_sum = ~0; /* Transactional Checksums */
        int                     descr_csum_size = 0;
        int                     block_error = 0;
+       bool                    need_check_commit_time = false;
+       __u64                   last_trans_commit_time = 0, commit_time;
 
        /*
         * First thing is to establish what we expect to find in the log
@@ -458,8 +477,8 @@ static int do_one_pass(journal_t *journal,
         */
 
        sb = journal->j_superblock;
-       next_commit_ID = ext2fs_be32_to_cpu(sb->s_sequence);
-       next_log_block = ext2fs_be32_to_cpu(sb->s_start);
+       next_commit_ID = be32_to_cpu(sb->s_sequence);
+       next_log_block = be32_to_cpu(sb->s_start);
 
        first_commit_ID = next_commit_ID;
        if (pass == PASS_SCAN)
@@ -477,7 +496,7 @@ static int do_one_pass(journal_t *journal,
        while (1) {
                int                     flags;
                char *                  tagp;
-               journal_block_tag_t *   tag;
+               journal_block_tag_t     tag;
                struct buffer_head *    obh;
                struct buffer_head *    nbh;
 
@@ -492,7 +511,9 @@ static int do_one_pass(journal_t *journal,
                                break;
 
                jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n",
-                         next_commit_ID, next_log_block, journal->j_last);
+                         next_commit_ID, next_log_block,
+                         jbd2_has_feature_fast_commit(journal) ?
+                         journal->j_fc_last : journal->j_last);
 
                /* Skip over each chunk of the transaction looking
                 * either the next descriptor block or the final commit
@@ -514,13 +535,13 @@ static int do_one_pass(journal_t *journal,
 
                tmp = (journal_header_t *)bh->b_data;
 
-               if (tmp->h_magic != ext2fs_cpu_to_be32(JBD2_MAGIC_NUMBER)) {
+               if (tmp->h_magic != cpu_to_be32(JBD2_MAGIC_NUMBER)) {
                        brelse(bh);
                        break;
                }
 
-               blocktype = ext2fs_be32_to_cpu(tmp->h_blocktype);
-               sequence = ext2fs_be32_to_cpu(tmp->h_sequence);
+               blocktype = be32_to_cpu(tmp->h_blocktype);
+               sequence = be32_to_cpu(tmp->h_sequence);
                jbd_debug(3, "Found magic %d, sequence %d\n",
                          blocktype, sequence);
 
@@ -540,11 +561,23 @@ static int do_one_pass(journal_t *journal,
                                descr_csum_size =
                                        sizeof(struct jbd2_journal_block_tail);
                        if (descr_csum_size > 0 &&
-                           !jbd2_descr_block_csum_verify(journal,
-                                                         bh->b_data)) {
-                               err = -EFSBADCRC;
-                               brelse(bh);
-                               goto failed;
+                           !jbd2_descriptor_block_csum_verify(journal,
+                                                              bh->b_data)) {
+                               /*
+                                * PASS_SCAN can see stale blocks due to lazy
+                                * journal init. Don't error out on those yet.
+                                */
+                               if (pass != PASS_SCAN) {
+                                       pr_err("JBD2: Invalid checksum recovering block %lu in log\n",
+                                              next_log_block);
+                                       err = -EFSBADCRC;
+                                       brelse(bh);
+                                       goto failed;
+                               }
+                               need_check_commit_time = true;
+                               jbd_debug(1,
+                                       "invalid descriptor block found in %lu\n",
+                                       next_log_block);
                        }
 
                        /* If it is a valid descriptor block, replay it
@@ -554,6 +587,7 @@ static int do_one_pass(journal_t *journal,
                        if (pass != PASS_REPLAY) {
                                if (pass == PASS_SCAN &&
                                    jbd2_has_feature_checksum(journal) &&
+                                   !need_check_commit_time &&
                                    !info->end_transaction) {
                                        if (calc_chksums(journal, bh,
                                                        &next_log_block,
@@ -579,8 +613,8 @@ static int do_one_pass(journal_t *journal,
                               <= journal->j_blocksize - descr_csum_size) {
                                unsigned long io_block;
 
-                               tag = (journal_block_tag_t *) tagp;
-                               flags = get_be16(&tag->t_flags);
+                               memcpy(&tag, tagp, sizeof(tag));
+                               flags = be16_to_cpu(tag.t_flags);
 
                                io_block = next_log_block++;
                                wrap(journal, next_log_block);
@@ -598,7 +632,7 @@ static int do_one_pass(journal_t *journal,
 
                                        J_ASSERT(obh != NULL);
                                        blocknr = read_tag_block(journal,
-                                                                tag);
+                                                                &tag);
 
                                        /* If the block has been
                                         * revoked, then we're all done
@@ -613,8 +647,8 @@ static int do_one_pass(journal_t *journal,
 
                                        /* Look for block corruption */
                                        if (!jbd2_block_tag_csum_verify(
-                                               journal, tag, obh->b_data,
-                                               ext2fs_be32_to_cpu(tmp->h_sequence))) {
+                       journal, &tag, (journal_block_tag3_t *)tagp,
+                       obh->b_data, be32_to_cpu(tmp->h_sequence))) {
                                                brelse(obh);
                                                success = -EFSBADCRC;
                                                printk(KERN_ERR "JBD2: Invalid "
@@ -644,9 +678,8 @@ static int do_one_pass(journal_t *journal,
                                        memcpy(nbh->b_data, obh->b_data,
                                                        journal->j_blocksize);
                                        if (flags & JBD2_FLAG_ESCAPE) {
-                                               __u32 magic = ext2fs_cpu_to_be32(JBD2_MAGIC_NUMBER);
-                                               memcpy(nbh->b_data, &magic,
-                                                      sizeof(magic));
+                                               *((__be32 *)nbh->b_data) =
+                                               cpu_to_be32(JBD2_MAGIC_NUMBER);
                                        }
 
                                        BUFFER_TRACE(nbh, "marking dirty");
@@ -686,7 +719,7 @@ static int do_one_pass(journal_t *journal,
                         *              | GO TO NEXT    "Journal Corruption"
                         *              | TRANSACTION
                         *              |
-                        * {(n+1)th transaction}
+                        * {(n+1)th transanction}
                         *              |
                         *       _______|______________
                         *      |                     |
@@ -703,20 +736,46 @@ static int do_one_pass(journal_t *journal,
                         *       mentioned conditions. Hence assume
                         *       "Interrupted Commit".)
                         */
+                       commit_time = be64_to_cpu(
+                               ((struct commit_header *)bh->b_data)->h_commit_sec);
+                       /*
+                        * If need_check_commit_time is set, it means we are in
+                        * PASS_SCAN and csum verify failed before. If
+                        * commit_time is increasing, it's the same journal,
+                        * otherwise it is stale journal block, just end this
+                        * recovery.
+                        */
+                       if (need_check_commit_time) {
+                               if (commit_time >= last_trans_commit_time) {
+                                       pr_err("JBD2: Invalid checksum found in transaction %u\n",
+                                              next_commit_ID);
+                                       err = -EFSBADCRC;
+                                       brelse(bh);
+                                       goto failed;
+                               }
+                       ignore_crc_mismatch:
+                               /*
+                                * It likely does not belong to same journal,
+                                * just end this recovery with success.
+                                */
+                               jbd_debug(1, "JBD2: Invalid checksum ignored in transaction %u, likely stale data\n",
+                                         next_commit_ID);
+                               brelse(bh);
+                               goto done;
+                       }
 
-                       /* Found an expected commit block: if checksums
-                        * are present verify them in PASS_SCAN; else not
+                       /*
+                        * Found an expected commit block: if checksums
+                        * are present, verify them in PASS_SCAN; else not
                         * much to do other than move on to the next sequence
-                        * number. */
+                        * number.
+                        */
                        if (pass == PASS_SCAN &&
                            jbd2_has_feature_checksum(journal)) {
-                               int chksum_err, chksum_seen;
                                struct commit_header *cbh =
                                        (struct commit_header *)bh->b_data;
                                unsigned found_chksum =
-                                       ext2fs_be32_to_cpu(cbh->h_chksum[0]);
-
-                               chksum_err = chksum_seen = 0;
+                                       be32_to_cpu(cbh->h_chksum[0]);
 
                                if (info->end_transaction) {
                                        journal->j_failed_commit =
@@ -725,42 +784,25 @@ static int do_one_pass(journal_t *journal,
                                        break;
                                }
 
-                               if (crc32_sum == found_chksum &&
-                                   cbh->h_chksum_type == JBD2_CRC32_CHKSUM &&
-                                   cbh->h_chksum_size ==
-                                               JBD2_CRC32_CHKSUM_SIZE)
-                                      chksum_seen = 1;
-                               else if (!(cbh->h_chksum_type == 0 &&
-                                            cbh->h_chksum_size == 0 &&
-                                            found_chksum == 0 &&
-                                            !chksum_seen))
-                               /*
-                                * If fs is mounted using an old kernel and then
-                                * kernel with journal_chksum is used then we
-                                * get a situation where the journal flag has
-                                * checksum flag set but checksums are not
-                                * present i.e chksum = 0, in the individual
-                                * commit blocks.
-                                * Hence to avoid checksum failures, in this
-                                * situation, this extra check is added.
-                                */
-                                               chksum_err = 1;
-
-                               if (chksum_err) {
-                                       info->end_transaction = next_commit_ID;
+                               /* Neither checksum match nor unused? */
+                               if (!((crc32_sum == found_chksum &&
+                                      cbh->h_chksum_type ==
+                                               JBD2_CRC32_CHKSUM &&
+                                      cbh->h_chksum_size ==
+                                               JBD2_CRC32_CHKSUM_SIZE) ||
+                                     (cbh->h_chksum_type == 0 &&
+                                      cbh->h_chksum_size == 0 &&
+                                      found_chksum == 0)))
+                                       goto chksum_error;
 
-                                       if (!jbd2_has_feature_async_commit(journal)) {
-                                               journal->j_failed_commit =
-                                                       next_commit_ID;
-                                               brelse(bh);
-                                               break;
-                                       }
-                               }
                                crc32_sum = ~0;
                        }
                        if (pass == PASS_SCAN &&
                            !jbd2_commit_block_csum_verify(journal,
                                                           bh->b_data)) {
+                       chksum_error:
+                               if (commit_time < last_trans_commit_time)
+                                       goto ignore_crc_mismatch;
                                info->end_transaction = next_commit_ID;
 
                                if (!jbd2_has_feature_async_commit(journal)) {
@@ -770,11 +812,24 @@ static int do_one_pass(journal_t *journal,
                                        break;
                                }
                        }
+                       if (pass == PASS_SCAN)
+                               last_trans_commit_time = commit_time;
                        brelse(bh);
                        next_commit_ID++;
                        continue;
 
                case JBD2_REVOKE_BLOCK:
+                       /*
+                        * Check revoke block crc in pass_scan, if csum verify
+                        * failed, check commit block time later.
+                        */
+                       if (pass == PASS_SCAN &&
+                           !jbd2_descriptor_block_csum_verify(journal,
+                                                              bh->b_data)) {
+                               jbd_debug(1, "JBD2: invalid revoke block found in %lu\n",
+                                         next_log_block);
+                               need_check_commit_time = true;
+                       }
                        /* If we aren't in the REVOKE pass, then we can
                         * just skip over this block. */
                        if (pass != PASS_REVOKE) {
@@ -819,6 +874,13 @@ static int do_one_pass(journal_t *journal,
                                success = -EIO;
                }
        }
+
+       if (jbd2_has_feature_fast_commit(journal) &&  pass != PASS_REVOKE) {
+               err = fc_do_one_pass(journal, info, pass);
+               if (err)
+                       success = err;
+       }
+
        if (block_error && success == 0)
                success = -EIO;
        return success;
@@ -827,26 +889,6 @@ static int do_one_pass(journal_t *journal,
        return err;
 }
 
-static int jbd2_revoke_block_csum_verify(journal_t *j,
-                                        void *buf)
-{
-       struct journal_revoke_tail *tail;
-       __u32 provided;
-       __u32 calculated;
-
-       if (!jbd2_journal_has_csum_v2or3(j))
-               return 1;
-
-       tail = (struct journal_revoke_tail *)((char *)buf + j->j_blocksize -
-                       sizeof(struct journal_revoke_tail));
-       provided = tail->r_checksum;
-       tail->r_checksum = 0;
-       calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
-       tail->r_checksum = provided;
-
-       return provided == ext2fs_cpu_to_be32(calculated);
-}
-
 /* Scan a revoke record, marking all blocks mentioned as revoked. */
 
 static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
@@ -860,13 +902,10 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
 
        header = (jbd2_journal_revoke_header_t *) bh->b_data;
        offset = sizeof(jbd2_journal_revoke_header_t);
-       rcount = ext2fs_be32_to_cpu(header->r_count);
-
-       if (!jbd2_revoke_block_csum_verify(journal, header))
-               return -EFSBADCRC;
+       rcount = be32_to_cpu(header->r_count);
 
        if (jbd2_journal_has_csum_v2or3(journal))
-               csum_size = sizeof(struct journal_revoke_tail);
+               csum_size = sizeof(struct jbd2_journal_block_tail);
        if (rcount > journal->j_blocksize - csum_size)
                return -EINVAL;
        max = rcount;
@@ -879,9 +918,9 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
                int err;
 
                if (record_len == 4)
-                       blocknr = ext2fs_be32_to_cpu(* ((__u32 *) (bh->b_data+offset)));
+                       blocknr = be32_to_cpu(* ((__be32 *) (bh->b_data+offset)));
                else
-                       blocknr = ext2fs_be64_to_cpu(* ((__u64 *) (bh->b_data+offset)));
+                       blocknr = be64_to_cpu(* ((__be64 *) (bh->b_data+offset)));
                offset += record_len;
                err = jbd2_journal_set_revoke(journal, blocknr, sequence);
                if (err)