#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
*extent = l - 1;
}
-int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,
- uint32_t *fblock)
+/**@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 */
int rc = ext4_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;
}
*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;
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;
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);
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)) {
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;
/* 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);
}
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 */
}
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 */
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;
struct ext4_block block;
rc =
- ext4_block_get(inode_ref->fs->bdev, &block, fblock);
+ ext4_block_get_noread(inode_ref->fs->bdev, &block, fblock);
if (rc != EOK) {
ext4_balloc_free_block(inode_ref, fblock);
return rc;
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 */
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 */
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;
struct ext4_block block;
- rc = ext4_block_get(inode_ref->fs->bdev, &block, new_fblock);
+ rc = ext4_block_get_noread(inode_ref->fs->bdev, &block, new_fblock);
if (rc != EOK)
return rc;
ext4_extent_header_set_max_entries_count(old_root->header,
limit);
+ ext4_extent_block_csum_set(inode_ref, old_root->header);
old_root->block.dirty = true;
/* Re-initialize new root metadata */
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) {
}
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);
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;
inode_ref->dirty = true;
}
+ ext4_extent_block_csum_set(inode_ref, path_ptr->header);
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 +=
inode_ref->dirty = true;
}
+ ext4_extent_block_csum_set(inode_ref, path_ptr->header);
path_ptr->block.dirty = true;
goto finish;
/* 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;
inode_ref->dirty = true;
}
+ ext4_extent_block_csum_set(inode_ref, path_ptr->header);
path_ptr->block.dirty = true;
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
/**
* @}