Introduce initial support of ext3/4 journalling.
[lwext4.git] / lwext4 / ext4_extent.c
index 38f51a7dd3d67aca92691d5b1ba753bf41e451bf..831cb4816dc7d690e784376c9b8c2bb630c5b48b 100644 (file)
 #include "ext4_extent.h"
 #include "ext4_inode.h"
 #include "ext4_super.h"
+#include "ext4_crc32c.h"
 #include "ext4_blockdev.h"
 #include "ext4_balloc.h"
+#include "ext4_fs.h"
 
 #include <string.h>
 #include <stdlib.h>
 
 #if !CONFIG_EXTENT_FULL
 
+static struct ext4_extent_header *ext_inode_hdr(struct ext4_inode *inode)
+{
+       return (struct ext4_extent_header *)inode->blocks;
+}
+
+static uint16_t ext_depth(struct ext4_inode *inode)
+{
+       return to_le16(ext_inode_hdr(inode)->depth);
+}
+
+static struct ext4_extent_tail *
+find_ext4_extent_tail(struct ext4_extent_header *eh)
+{
+       return (struct ext4_extent_tail *)(((char *)eh) +
+                                          EXT4_EXTENT_TAIL_OFFSET(eh));
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_ext_block_csum(struct ext4_inode_ref *inode_ref,
+                                   struct ext4_extent_header *eh)
+{
+       uint32_t checksum = 0;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t ino_index = to_le32(inode_ref->index);
+               uint32_t ino_gen =
+                       to_le32(ext4_inode_get_generation(inode_ref->inode));
+               /* First calculate crc32 checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
+                               sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against inode number
+                * and inode generation */
+               checksum = ext4_crc32c(checksum, &ino_index,
+                                    sizeof(ino_index));
+               checksum = ext4_crc32c(checksum, &ino_gen,
+                                    sizeof(ino_gen));
+               /* Finally calculate crc32 checksum against
+                * the entire extent block up to the checksum field */
+               checksum = ext4_crc32c(checksum, eh,
+                               EXT4_EXTENT_TAIL_OFFSET(eh));
+       }
+       return checksum;
+}
+#else
+#define ext4_ext_block_csum(...) 0
+#endif
+
+static void ext4_extent_block_csum_set(struct ext4_inode_ref *inode_ref,
+                                      struct ext4_extent_header *eh)
+{
+       struct ext4_extent_tail *tail;
+       if (!ext4_sb_feature_ro_com(&inode_ref->fs->sb,
+                                   EXT4_FRO_COM_METADATA_CSUM))
+               return;
+
+       if (to_le16(eh->depth) < ext_depth(inode_ref->inode)) {
+               tail = find_ext4_extent_tail(eh);
+               tail->et_checksum = to_le32(ext4_ext_block_csum(inode_ref, eh));
+       }
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+ext4_extent_verify_block_csum(struct ext4_inode_ref *inode_ref,
+                             struct ext4_block *block)
+{
+       struct ext4_extent_header *eh;
+       struct ext4_extent_tail *tail;
+       eh = (struct ext4_extent_header *)block->data;
+       if (!ext4_sb_feature_ro_com(&inode_ref->fs->sb,
+                                   EXT4_FRO_COM_METADATA_CSUM))
+               return true;
+
+       if (to_le16(eh->depth) < ext_depth(inode_ref->inode)) {
+               tail = find_ext4_extent_tail(eh);
+               return tail->et_checksum ==
+                       to_le32(ext4_ext_block_csum(inode_ref, eh));
+       }
+
+       return true;
+}
+#else
+#define ext4_extent_verify_block_csum(...) true
+#endif
+
 /**@brief Binary search in extent index node.
  * @param header Extent header of index node
  * @param index  Output value - found index will be set here
@@ -169,9 +257,16 @@ ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,
                                return rc;
                }
 
-               int rc = ext4_block_get(inode_ref->fs->bdev, &block, child);
+               int rc = ext4_trans_block_get(inode_ref->fs->bdev, &block, child);
                if (rc != EOK)
                        return rc;
+               if (!ext4_extent_verify_block_csum(inode_ref,
+                                                  &block)) {
+                       ext4_dbg(DEBUG_EXTENT,
+                                DBG_WARN "Extent block checksum failed."
+                                "Blocknr: %" PRIu64"\n",
+                                child);
+               }
 
                header = (struct ext4_extent_header *)block.data;
        }
@@ -247,10 +342,18 @@ static int ext4_extent_find_extent(struct ext4_inode_ref *inode_ref,
                    ext4_extent_index_get_leaf(tmp_path[pos].index);
 
                struct ext4_block block;
-               rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);
+               rc = ext4_trans_block_get(inode_ref->fs->bdev, &block, fblock);
                if (rc != EOK)
                        goto cleanup;
 
+               if (!ext4_extent_verify_block_csum(inode_ref,
+                                                  &block)) {
+                       ext4_dbg(DEBUG_EXTENT,
+                                DBG_WARN "Extent block checksum failed."
+                                "Blocknr: %" PRIu64"\n",
+                                fblock);
+               }
+
                pos++;
 
                eh = (struct ext4_extent_header *)block.data;
@@ -316,10 +419,18 @@ static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref,
        ext4_fsblk_t fblock = ext4_extent_index_get_leaf(index);
        uint32_t i;
        struct ext4_block block;
-       int rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);
+       int rc = ext4_trans_block_get(inode_ref->fs->bdev, &block, fblock);
        if (rc != EOK)
                return rc;
 
+       if (!ext4_extent_verify_block_csum(inode_ref,
+                               &block)) {
+               ext4_dbg(DEBUG_EXTENT,
+                        DBG_WARN "Extent block checksum failed."
+                        "Blocknr: %" PRIu64"\n",
+                        fblock);
+       }
+
        struct ext4_extent_header *header = (void *)block.data;
 
        if (ext4_extent_header_get_depth(header)) {
@@ -358,7 +469,7 @@ static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref,
 int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
                             ext4_lblk_t to)
 {
-       if (to != (ext4_lblk_t)-1)
+       if (to != EXT_MAX_BLOCKS)
                return ENOTSUP;
 
        /* Find the first extent to modify */
@@ -422,7 +533,8 @@ int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
        }
 
        ext4_extent_header_set_entries_count(path_ptr->header, entries);
-       path_ptr->block.dirty = true;
+       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+       ext4_trans_set_block_dirty(path_ptr->block.buf);
 
        /* If leaf node is empty, parent entry must be modified */
        bool remove_parent_record = false;
@@ -463,7 +575,8 @@ int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
                }
 
                ext4_extent_header_set_entries_count(path_ptr->header, entries);
-               path_ptr->block.dirty = true;
+               ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+               ext4_trans_set_block_dirty(path_ptr->block.buf);
 
                /* Free the node if it is empty */
                if ((entries == 0) && (path_ptr != path)) {
@@ -538,7 +651,7 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
 
                        struct ext4_block block;
                        rc =
-                           ext4_block_get(inode_ref->fs->bdev, &block, fblock);
+                           ext4_trans_block_get_noread(inode_ref->fs->bdev, &block, fblock);
                        if (rc != EOK) {
                                ext4_balloc_free_block(inode_ref, fblock);
                                return rc;
@@ -590,7 +703,8 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                                                     path_ptr->depth);
                        ext4_extent_header_set_generation(path_ptr->header, 0);
 
-                       path_ptr->block.dirty = true;
+                       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+                       ext4_trans_set_block_dirty(path_ptr->block.buf);
 
                        /* Jump to the preceding item */
                        path_ptr--;
@@ -615,7 +729,8 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
 
                        ext4_extent_header_set_entries_count(path_ptr->header,
                                                             entries + 1);
-                       path_ptr->block.dirty = true;
+                       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+                       ext4_trans_set_block_dirty(path_ptr->block.buf);
 
                        /* No more splitting needed */
                        return EOK;
@@ -640,7 +755,7 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                        return rc;
 
                struct ext4_block block;
-               rc = ext4_block_get(inode_ref->fs->bdev, &block, new_fblock);
+               rc = ext4_trans_block_get_noread(inode_ref->fs->bdev, &block, new_fblock);
                if (rc != EOK)
                        return rc;
 
@@ -697,7 +812,8 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                ext4_extent_header_set_max_entries_count(old_root->header,
                                                         limit);
 
-               old_root->block.dirty = true;
+               ext4_extent_block_csum_set(inode_ref, old_root->header);
+               ext4_trans_set_block_dirty(old_root->block.buf);
 
                /* Re-initialize new root metadata */
                new_root->depth = root_depth + 1;
@@ -713,7 +829,9 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                ext4_extent_index_set_first_block(new_root->index, 0);
                ext4_extent_index_set_leaf(new_root->index, new_fblock);
 
-               new_root->block.dirty = true;
+               /* Since new_root belongs to on-disk inode,
+                * we don't do checksum here */
+               ext4_trans_set_block_dirty(new_root->block.buf);
        } else {
                if (path->depth) {
                        path->index =
@@ -728,7 +846,9 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                }
 
                ext4_extent_header_set_entries_count(path->header, entries + 1);
-               path->block.dirty = true;
+               /* Since new_root belongs to on-disk inode,
+                * we don't do checksum here */
+               ext4_trans_set_block_dirty(path->block.buf);
        }
 
        return EOK;
@@ -807,7 +927,8 @@ ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
                                inode_ref->dirty = true;
                        }
 
-                       path_ptr->block.dirty = true;
+                       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+                       ext4_trans_set_block_dirty(path_ptr->block.buf);
 
                        goto finish;
                } else {
@@ -848,7 +969,8 @@ ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
                                inode_ref->dirty = true;
                        }
 
-                       path_ptr->block.dirty = true;
+                       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+                       ext4_trans_set_block_dirty(path_ptr->block.buf);
 
                        goto finish;
                }
@@ -888,7 +1010,8 @@ append_extent:
                inode_ref->dirty = true;
        }
 
-       path_ptr->block.dirty = true;
+       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
+       ext4_trans_set_block_dirty(path_ptr->block.buf);
 
 finish:
        /* Set return values */