Whamcloud - gitweb
Update release notes, etc., for the 1.45.6 release
[tools/e2fsprogs.git] / e2fsck / recovery.c
index 8fdd934..81110e4 100644 (file)
@@ -124,6 +124,27 @@ 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
@@ -140,7 +161,7 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
 
        if (offset >= journal->j_maxlen) {
                printk(KERN_ERR "JBD2: corrupted journal superblock\n");
-               return -EIO;
+               return -EFSCORRUPTED;
        }
 
        err = journal_bmap(journal, offset, &blocknr);
@@ -181,10 +202,10 @@ static int jbd2_descr_block_csum_verify(journal_t *j,
        __u32 provided;
        __u32 calculated;
 
-       if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+       if (!journal_has_csum_v2or3(j))
                return 1;
 
-       tail = (struct journal_block_tail *)(buf + j->j_blocksize -
+       tail = (struct journal_block_tail *)((char *)buf + j->j_blocksize -
                        sizeof(struct journal_block_tail));
        provided = tail->t_checksum;
        tail->t_checksum = 0;
@@ -205,7 +226,7 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
        int                     nr = 0, size = journal->j_blocksize;
        int                     tag_bytes = journal_tag_bytes(journal);
 
-       if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2))
+       if (journal_has_csum_v2or3(journal))
                size -= sizeof(struct journal_block_tail);
 
        tagp = &bh->b_data[sizeof(journal_header_t)];
@@ -215,10 +236,10 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
 
                nr++;
                tagp += tag_bytes;
-               if (!(tag->t_flags & ext2fs_cpu_to_be16(JFS_FLAG_SAME_UUID)))
+               if (!(get_be16(&tag->t_flags) & JFS_FLAG_SAME_UUID))
                        tagp += 16;
 
-               if (tag->t_flags & ext2fs_cpu_to_be16(JFS_FLAG_LAST_TAG))
+               if (get_be16(&tag->t_flags) & JFS_FLAG_LAST_TAG)
                        break;
        }
 
@@ -304,7 +325,7 @@ int journal_recover(journal_t *journal)
  * Locate any valid recovery information from the journal and set up the
  * journal structures in memory to ignore it (presumably because the
  * caller has evidence that it is out of date).
- * This function does'nt appear to be exorted..
+ * This function doesn't appear to be exported..
  *
  * We perform one pass over the journal to allow us to tell the user how
  * much recovery information is being erased, and to let us initialise
@@ -338,11 +359,12 @@ int journal_skip_recovery(journal_t *journal)
        return err;
 }
 
-static inline unsigned long long read_tag_block(int tag_bytes, journal_block_tag_t *tag)
+static inline unsigned long long read_tag_block(journal_t *journal,
+                                               journal_block_tag_t *tag)
 {
-       unsigned long long block = ext2fs_be32_to_cpu(tag->t_blocknr);
-       if (tag_bytes > JFS_TAG_SIZE32)
-               block |= (u64)ext2fs_be32_to_cpu(tag->t_blocknr_high) << 32;
+       unsigned long long block = get_be32(&tag->t_blocknr);
+       if (jfs_has_feature_64bit(journal))
+               block |= (u64)get_be32(&tag->t_blocknr_high) << 32;
        return block;
 }
 
@@ -384,7 +406,7 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
        __u32 provided;
        __u32 calculated;
 
-       if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+       if (!journal_has_csum_v2or3(j))
                return 1;
 
        h = buf;
@@ -399,17 +421,21 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
 static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
                                      void *buf, __u32 sequence)
 {
+       journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag;
        __u32 csum32;
        __u32 seq;
 
-       if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+       if (!journal_has_csum_v2or3(j))
                return 1;
 
        seq = ext2fs_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);
 
-       return tag->t_checksum == ext2fs_cpu_to_be16(csum32);
+       if (jfs_has_feature_csum3(j))
+               return get_be32(&tag3->t_checksum) == csum32;
+
+       return get_be16(&tag->t_checksum) == (csum32 & 0xFFFF);
 }
 
 static int do_one_pass(journal_t *journal,
@@ -426,6 +452,7 @@ static int do_one_pass(journal_t *journal,
        int                     tag_bytes = journal_tag_bytes(journal);
        __u32                   crc32_sum = ~0; /* Transactional Checksums */
        int                     descr_csum_size = 0;
+       int                     block_error = 0;
 
        /*
         * First thing is to establish what we expect to find in the log
@@ -512,14 +539,14 @@ static int do_one_pass(journal_t *journal,
                switch(blocktype) {
                case JFS_DESCRIPTOR_BLOCK:
                        /* Verify checksum first */
-                       if (JFS_HAS_INCOMPAT_FEATURE(journal,
-                                       JFS_FEATURE_INCOMPAT_CSUM_V2))
+                       if (journal_has_csum_v2or3(journal))
                                descr_csum_size =
                                        sizeof(struct journal_block_tail);
                        if (descr_csum_size > 0 &&
                            !jbd2_descr_block_csum_verify(journal,
                                                          bh->b_data)) {
-                               err = -EIO;
+                               err = -EFSBADCRC;
+                               brelse(bh);
                                goto failed;
                        }
 
@@ -529,8 +556,7 @@ static int do_one_pass(journal_t *journal,
                         * just skip over the blocks it describes. */
                        if (pass != PASS_REPLAY) {
                                if (pass == PASS_SCAN &&
-                                   JFS_HAS_COMPAT_FEATURE(journal,
-                                           JFS_FEATURE_COMPAT_CHECKSUM) &&
+                                   jfs_has_feature_checksum(journal) &&
                                    !info->end_transaction) {
                                        if (calc_chksums(journal, bh,
                                                        &next_log_block,
@@ -557,7 +583,7 @@ static int do_one_pass(journal_t *journal,
                                unsigned long io_block;
 
                                tag = (journal_block_tag_t *) tagp;
-                               flags = ext2fs_be16_to_cpu(tag->t_flags);
+                               flags = get_be16(&tag->t_flags);
 
                                io_block = next_log_block++;
                                wrap(journal, next_log_block);
@@ -574,7 +600,7 @@ static int do_one_pass(journal_t *journal,
                                        unsigned long long blocknr;
 
                                        J_ASSERT(obh != NULL);
-                                       blocknr = read_tag_block(tag_bytes,
+                                       blocknr = read_tag_block(journal,
                                                                 tag);
 
                                        /* If the block has been
@@ -593,12 +619,13 @@ static int do_one_pass(journal_t *journal,
                                                journal, tag, obh->b_data,
                                                ext2fs_be32_to_cpu(tmp->h_sequence))) {
                                                brelse(obh);
-                                               success = -EIO;
+                                               success = -EFSBADCRC;
                                                printk(KERN_ERR "JBD2: Invalid "
                                                       "checksum recovering "
                                                       "block %llu in log\n",
                                                       blocknr);
-                                               continue;
+                                               block_error = 1;
+                                               goto skip_write;
                                        }
 
                                        /* Find a buffer for the new
@@ -620,8 +647,9 @@ static int do_one_pass(journal_t *journal,
                                        memcpy(nbh->b_data, obh->b_data,
                                                        journal->j_blocksize);
                                        if (flags & JFS_FLAG_ESCAPE) {
-                                               *((__u32 *)nbh->b_data) =
-                                               ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER);
+                                               __u32 magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER);
+                                               memcpy(nbh->b_data, &magic,
+                                                      sizeof(magic));
                                        }
 
                                        BUFFER_TRACE(nbh, "marking dirty");
@@ -661,7 +689,7 @@ static int do_one_pass(journal_t *journal,
                         *              | GO TO NEXT    "Journal Corruption"
                         *              | TRANSACTION
                         *              |
-                        * {(n+1)th transanction}
+                        * {(n+1)th transaction}
                         *              |
                         *       _______|______________
                         *      |                     |
@@ -684,8 +712,7 @@ static int do_one_pass(journal_t *journal,
                         * much to do other than move on to the next sequence
                         * number. */
                        if (pass == PASS_SCAN &&
-                           JFS_HAS_COMPAT_FEATURE(journal,
-                                   JFS_FEATURE_COMPAT_CHECKSUM)) {
+                           jfs_has_feature_checksum(journal)) {
                                int chksum_err, chksum_seen;
                                struct commit_header *cbh =
                                        (struct commit_header *)bh->b_data;
@@ -725,8 +752,7 @@ static int do_one_pass(journal_t *journal,
                                if (chksum_err) {
                                        info->end_transaction = next_commit_ID;
 
-                                       if (!JFS_HAS_INCOMPAT_FEATURE(journal,
-                                          JFS_FEATURE_INCOMPAT_ASYNC_COMMIT)){
+                                       if (!jfs_has_feature_async_commit(journal)){
                                                journal->j_failed_commit =
                                                        next_commit_ID;
                                                brelse(bh);
@@ -740,8 +766,7 @@ static int do_one_pass(journal_t *journal,
                                                           bh->b_data)) {
                                info->end_transaction = next_commit_ID;
 
-                               if (!JFS_HAS_INCOMPAT_FEATURE(journal,
-                                    JFS_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
+                               if (!jfs_has_feature_async_commit(journal)) {
                                        journal->j_failed_commit =
                                                next_commit_ID;
                                        brelse(bh);
@@ -797,7 +822,8 @@ static int do_one_pass(journal_t *journal,
                                success = -EIO;
                }
        }
-
+       if (block_error && success == 0)
+               success = -EIO;
        return success;
 
  failed:
@@ -811,10 +837,10 @@ static int jbd2_revoke_block_csum_verify(journal_t *j,
        __u32 provided;
        __u32 calculated;
 
-       if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+       if (!journal_has_csum_v2or3(j))
                return 1;
 
-       tail = (struct journal_revoke_tail *)(buf + j->j_blocksize -
+       tail = (struct journal_revoke_tail *)((char *)buf + j->j_blocksize -
                        sizeof(struct journal_revoke_tail));
        provided = tail->r_checksum;
        tail->r_checksum = 0;
@@ -831,16 +857,24 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
 {
        journal_revoke_header_t *header;
        int offset, max;
+       unsigned csum_size = 0;
+       __u32 rcount;
        int record_len = 4;
 
        header = (journal_revoke_header_t *) bh->b_data;
        offset = sizeof(journal_revoke_header_t);
-       max = ext2fs_be32_to_cpu(header->r_count);
+       rcount = ext2fs_be32_to_cpu(header->r_count);
 
        if (!jbd2_revoke_block_csum_verify(journal, header))
+               return -EFSBADCRC;
+
+       if (journal_has_csum_v2or3(journal))
+               csum_size = sizeof(struct journal_revoke_tail);
+       if (rcount > journal->j_blocksize - csum_size)
                return -EINVAL;
+       max = rcount;
 
-       if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
+       if (jfs_has_feature_64bit(journal))
                record_len = 8;
 
        while (offset + record_len <= max) {