FIX: ext4_extent_full.c and ext4_extent.c lack endian conversion when doing checksum.
[lwext4.git] / lwext4 / ext4_extent.c
index eb3d8162367e70e36287afad5b95c1b0ec207143..5e1ec8c5e9e97d827be3413b0ef685f63d1369f0 100644 (file)
 #include "ext4_super.h"
 #include "ext4_blockdev.h"
 #include "ext4_balloc.h"
+#include "ext4_fs.h"
 
 #include <string.h>
 #include <stdlib.h>
 
 #if !CONFIG_EXTENT_FULL
 
-uint32_t ext4_extent_get_first_block(struct ext4_extent *extent)
-{
-       return to_le32(extent->first_block);
-}
-
-void ext4_extent_set_first_block(struct ext4_extent *extent, uint32_t iblock)
-{
-       extent->first_block = to_le32(iblock);
-}
-
-uint16_t ext4_extent_get_block_count(struct ext4_extent *extent)
-{
-       if (EXT4_EXT_IS_UNWRITTEN(extent))
-               return EXT4_EXT_GET_LEN_UNWRITTEN(extent);
-       else
-               return EXT4_EXT_GET_LEN(extent);
-}
-
-void ext4_extent_set_block_count(struct ext4_extent *extent, uint16_t count,
-                                bool unwritten)
-{
-       EXT4_EXT_SET_LEN(extent, count);
-       if (unwritten)
-               EXT4_EXT_SET_UNWRITTEN(extent);
-}
-
-uint64_t ext4_extent_get_start(struct ext4_extent *extent)
-{
-       return ((uint64_t)to_le16(extent->start_hi)) << 32 |
-              ((uint64_t)to_le32(extent->start_lo));
-}
-
-void ext4_extent_set_start(struct ext4_extent *extent, uint64_t fblock)
-{
-       extent->start_lo = to_le32((fblock << 32) >> 32);
-       extent->start_hi = to_le16((uint16_t)(fblock >> 32));
-}
-
-uint32_t ext4_extent_index_get_first_block(struct ext4_extent_index *index)
-{
-       return to_le32(index->first_block);
-}
-
-void ext4_extent_index_set_first_block(struct ext4_extent_index *index,
-                                      uint32_t iblock)
-{
-       index->first_block = to_le32(iblock);
-}
-
-uint64_t ext4_extent_index_get_leaf(struct ext4_extent_index *index)
-{
-       return ((uint64_t)to_le16(index->leaf_hi)) << 32 |
-              ((uint64_t)to_le32(index->leaf_lo));
-}
-
-void ext4_extent_index_set_leaf(struct ext4_extent_index *index,
-                               uint64_t fblock)
-{
-       index->leaf_lo = to_le32((fblock << 32) >> 32);
-       index->leaf_hi = to_le16((uint16_t)(fblock >> 32));
-}
-
-uint16_t ext4_extent_header_get_magic(struct ext4_extent_header *header)
-{
-       return to_le16(header->magic);
-}
-
-void ext4_extent_header_set_magic(struct ext4_extent_header *header,
-                                 uint16_t magic)
-{
-       header->magic = to_le16(magic);
-}
-
-uint16_t ext4_extent_header_get_entries_count(struct ext4_extent_header *header)
-{
-       return to_le16(header->entries_count);
-}
-
-void ext4_extent_header_set_entries_count(struct ext4_extent_header *header,
-                                         uint16_t count)
-{
-       header->entries_count = to_le16(count);
-}
-
-uint16_t
-ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header)
-{
-       return to_le16(header->max_entries_count);
-}
-
-void ext4_extent_header_set_max_entries_count(struct ext4_extent_header *header,
-                                             uint16_t max_count)
-{
-       header->max_entries_count = to_le16(max_count);
-}
-
-uint16_t ext4_extent_header_get_depth(struct ext4_extent_header *header)
-{
-       return to_le16(header->depth);
-}
-
-void ext4_extent_header_set_depth(struct ext4_extent_header *header,
-                                 uint16_t depth)
-{
-       header->depth = to_le16(depth);
-}
-
-uint32_t ext4_extent_header_get_generation(struct ext4_extent_header *header)
-{
-       return to_le32(header->generation);
-}
-
-void ext4_extent_header_set_generation(struct ext4_extent_header *header,
-                                      uint32_t generation)
-{
-       header->generation = to_le32(generation);
-}
-
 /**@brief Binary search in extent index node.
  * @param header Extent header of index node
  * @param index  Output value - found index will be set here
@@ -241,8 +124,57 @@ static void ext4_extent_binsearch(struct ext4_extent_header *header,
        *extent = l - 1;
 }
 
-int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,
-                          uint32_t *fblock)
+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_has_feature_read_only(sb,
+                               EXT4_FEATURE_RO_COMPAT_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(~0, 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;
+}
+
+/*
+ * BIG FAT NOTES:
+ *       Currently we do not verify the checksum of extent
+ *       (only the case in ext4_extent.c)
+ */
+
+static void ext4_extent_block_csum_set(struct ext4_inode_ref *inode_ref,
+                                      struct ext4_extent_header *eh)
+{
+       struct ext4_extent_tail *tail;
+
+       tail = find_ext4_extent_tail(eh);
+       tail->et_checksum = to_le32(ext4_ext_block_csum(inode_ref, eh));
+}
+
+/**@brief Get physical block in the extent tree by logical block number.
+ * There is no need to save path in the tree during this algorithm.
+ * @param inode_ref I-node to load block from
+ * @param iblock    Logical block number to find
+ * @param fblock    Output value for physical block number
+ * @return Error code*/
+static int
+ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,
+                          ext4_fsblk_t *fblock)
 {
        int rc;
        /* Compute bound defined by i-node size */
@@ -296,7 +228,7 @@ int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,
                *fblock = 0;
        } else {
                /* Compute requested physical block address */
-               uint32_t phys_block;
+               ext4_fsblk_t phys_block;
                uint32_t first = ext4_extent_get_first_block(extent);
                phys_block = ext4_extent_get_start(extent) + iblock - first;
 
@@ -424,7 +356,7 @@ static int ext4_extent_release(struct ext4_inode_ref *inode_ref,
 static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref,
                                      struct ext4_extent_index *index)
 {
-       uint32_t fblock = ext4_extent_index_get_leaf(index);
+       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);
@@ -466,13 +398,16 @@ static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref,
        return ext4_balloc_free_block(inode_ref, fblock);
 }
 
-int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref,
-                                   uint32_t iblock_from)
+int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
+                            ext4_lblk_t to)
 {
+       if (to != EXT_MAX_BLOCKS)
+               return ENOTSUP;
+
        /* Find the first extent to modify */
        struct ext4_extent_path *path;
        uint16_t i;
-       int rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
+       int rc = ext4_extent_find_extent(inode_ref, from, &path);
        if (rc != EOK)
                return rc;
 
@@ -485,8 +420,8 @@ int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref,
 
        /* First extent maybe released partially */
        uint32_t first_iblock = ext4_extent_get_first_block(path_ptr->extent);
-       uint32_t first_fblock = ext4_extent_get_start(path_ptr->extent) +
-                               iblock_from - first_iblock;
+       ext4_fsblk_t first_fblock = ext4_extent_get_start(path_ptr->extent) +
+                               from - first_iblock;
 
        uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
 
@@ -530,6 +465,7 @@ int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref,
        }
 
        ext4_extent_header_set_entries_count(path_ptr->header, entries);
+       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
        path_ptr->block.dirty = true;
 
        /* If leaf node is empty, parent entry must be modified */
@@ -571,6 +507,7 @@ int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref,
                }
 
                ext4_extent_header_set_entries_count(path_ptr->header, entries);
+               ext4_extent_block_csum_set(inode_ref, path_ptr->header);
                path_ptr->block.dirty = true;
 
                /* Free the node if it is empty */
@@ -635,8 +572,12 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
 
                if (entries == limit) {
                        /* Full node - allocate block for new one */
-                       uint32_t fblock;
-                       int rc = ext4_balloc_alloc_block(inode_ref, &fblock);
+                       ext4_fsblk_t goal, fblock;
+                       int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+                       if (rc != EOK)
+                               return rc;
+
+                       rc = ext4_balloc_alloc_block(inode_ref, goal, &fblock);
                        if (rc != EOK)
                                return rc;
 
@@ -694,6 +635,7 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                                                     path_ptr->depth);
                        ext4_extent_header_set_generation(path_ptr->header, 0);
 
+                       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
                        path_ptr->block.dirty = true;
 
                        /* Jump to the preceding item */
@@ -719,6 +661,7 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
 
                        ext4_extent_header_set_entries_count(path_ptr->header,
                                                             entries + 1);
+                       ext4_extent_block_csum_set(inode_ref, path_ptr->header);
                        path_ptr->block.dirty = true;
 
                        /* No more splitting needed */
@@ -734,8 +677,12 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
        uint16_t limit = ext4_extent_header_get_max_entries_count(path->header);
 
        if (entries == limit) {
-               uint32_t new_fblock;
-               int rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
+               ext4_fsblk_t goal, new_fblock;
+               int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+               if (rc != EOK)
+                       return rc;
+
+               rc = ext4_balloc_alloc_block(inode_ref, goal, &new_fblock);
                if (rc != EOK)
                        return rc;
 
@@ -797,6 +744,7 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                ext4_extent_header_set_max_entries_count(old_root->header,
                                                         limit);
 
+               ext4_extent_block_csum_set(inode_ref, ext_block_hdr(old_root->block));
                old_root->block.dirty = true;
 
                /* Re-initialize new root metadata */
@@ -813,6 +761,8 @@ 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);
 
+               /* Since new_root belongs to on-disk inode,
+                * we don't do checksum here */
                new_root->block.dirty = true;
        } else {
                if (path->depth) {
@@ -828,16 +778,29 @@ static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
                }
 
                ext4_extent_header_set_entries_count(path->header, entries + 1);
+               /* Since new_root belongs to on-disk inode,
+                * we don't do checksum here */
                path->block.dirty = true;
        }
 
        return EOK;
 }
 
-int ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
-                            uint32_t *fblock, bool update_size)
+/**@brief Append data block to the i-node.
+ * This function allocates data block, tries to append it
+ * to some existing extent or creates new extents.
+ * It includes possible extent tree modifications (splitting).
+ * @param inode_ref I-node to append block to
+ * @param iblock    Output logical number of newly allocated block
+ * @param fblock    Output physical block address of newly allocated block
+ *
+ * @return Error code*/
+static int
+ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
+                            ext4_fsblk_t *fblock, bool update_size)
 {
        uint16_t i;
+       ext4_fsblk_t goal;
        struct ext4_sblock *sb = &inode_ref->fs->sb;
        uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
        uint32_t block_size = ext4_sb_get_block_size(sb);
@@ -869,12 +832,16 @@ int ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
        uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
        uint16_t block_limit = (1 << 15);
 
-       uint32_t phys_block = 0;
+       ext4_fsblk_t phys_block = 0;
        if (block_count < block_limit) {
                /* There is space for new block in the extent */
                if (block_count == 0) {
+                       int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+                       if (rc != EOK)
+                               goto finish;
+
                        /* Existing extent is empty */
-                       rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
+                       rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block);
                        if (rc != EOK)
                                goto finish;
 
@@ -892,10 +859,16 @@ int ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
                                inode_ref->dirty = true;
                        }
 
+                       ext4_extent_block_csum_set(inode_ref, ext_block_hdr(path_ptr->block));
                        path_ptr->block.dirty = true;
 
                        goto finish;
                } else {
+                       ext4_fsblk_t goal;
+                       int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+                       if (rc != EOK)
+                               goto finish;
+
                        /* Existing extent contains some blocks */
                        phys_block = ext4_extent_get_start(path_ptr->extent);
                        phys_block +=
@@ -928,6 +901,7 @@ int ext4_extent_append_block(struct ext4_inode_ref *inode_ref, uint32_t *iblock,
                                inode_ref->dirty = true;
                        }
 
+                       ext4_extent_block_csum_set(inode_ref, ext_block_hdr(path_ptr->block));
                        path_ptr->block.dirty = true;
 
                        goto finish;
@@ -938,8 +912,12 @@ append_extent:
        /* Append new extent to the tree */
        phys_block = 0;
 
+       rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+       if (rc != EOK)
+               goto finish;
+
        /* Allocate new data block */
-       rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
+       rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block);
        if (rc != EOK)
                goto finish;
 
@@ -964,6 +942,7 @@ append_extent:
                inode_ref->dirty = true;
        }
 
+       ext4_extent_block_csum_set(inode_ref, ext_block_hdr(path_ptr->block));
        path_ptr->block.dirty = true;
 
 finish:
@@ -990,6 +969,28 @@ finish:
        return rc;
 }
 
+int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_fsblk_t iblock,
+                          ext4_lblk_t max_blocks, ext4_fsblk_t *result, bool create,
+                          ext4_lblk_t *blocks_count)
+{
+       uint32_t iblk = iblock;
+       ext4_fsblk_t fblk = 0;
+       int r;
+
+       if (blocks_count)
+               return ENOTSUP;
+       if (max_blocks != 1)
+               return ENOTSUP;
+
+       if (!create)
+               r = ext4_extent_find_block(inode_ref, iblk, &fblk);
+       else
+               r = ext4_extent_append_block(inode_ref, &iblk, &fblk, false);
+
+       *result = fblk;
+       return r;
+}
+
 #endif
 /**
  * @}