X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=lwext4%2Fext4_extent.c;h=e5ec5f85dce656363dba592db5dfb9ee9035182c;hb=97134feca27a7b1b13e63c280861940dd7a717de;hp=eb3d8162367e70e36287afad5b95c1b0ec207143;hpb=0d951d61e5ae9c64091daf6f469dc71af6175daf;p=lwext4.git diff --git a/lwext4/ext4_extent.c b/lwext4/ext4_extent.c index eb3d816..e5ec5f8 100644 --- a/lwext4/ext4_extent.c +++ b/lwext4/ext4_extent.c @@ -43,131 +43,101 @@ #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 #include #if !CONFIG_EXTENT_FULL -uint32_t ext4_extent_get_first_block(struct ext4_extent *extent) +static struct ext4_extent_header *ext_inode_hdr(struct ext4_inode *inode) { - return to_le32(extent->first_block); + return (struct ext4_extent_header *)inode->blocks; } -void ext4_extent_set_first_block(struct ext4_extent *extent, uint32_t iblock) +static uint16_t ext_depth(struct ext4_inode *inode) { - extent->first_block = to_le32(iblock); + return to_le16(ext_inode_hdr(inode)->depth); } -uint16_t ext4_extent_get_block_count(struct ext4_extent *extent) +static struct ext4_extent_tail * +find_ext4_extent_tail(struct ext4_extent_header *eh) { - 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)); + return (struct ext4_extent_tail *)(((char *)eh) + + EXT4_EXTENT_TAIL_OFFSET(eh)); } -void ext4_extent_index_set_leaf(struct ext4_extent_index *index, - uint64_t fblock) +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_ext_block_csum(struct ext4_inode_ref *inode_ref, + struct ext4_extent_header *eh) { - 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); -} + uint32_t checksum = 0; + struct ext4_sblock *sb = &inode_ref->fs->sb; -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); + 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 -uint16_t ext4_extent_header_get_depth(struct ext4_extent_header *header) +static void ext4_extent_block_csum_set(struct ext4_inode_ref *inode_ref, + struct ext4_extent_header *eh) { - return to_le16(header->depth); -} + struct ext4_extent_tail *tail; + if (!ext4_sb_feature_ro_com(&inode_ref->fs->sb, + EXT4_FRO_COM_METADATA_CSUM)) + return; -void ext4_extent_header_set_depth(struct ext4_extent_header *header, - uint16_t depth) -{ - header->depth = to_le16(depth); + 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)); + } } -uint32_t ext4_extent_header_get_generation(struct ext4_extent_header *header) +#if CONFIG_META_CSUM_ENABLE +static bool +ext4_extent_verify_block_csum(struct ext4_inode_ref *inode_ref, + struct ext4_block *block) { - return to_le32(header->generation); -} + 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)); + } -void ext4_extent_header_set_generation(struct ext4_extent_header *header, - uint32_t generation) -{ - header->generation = to_le32(generation); + 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 @@ -241,8 +211,15 @@ 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) +/**@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 */ @@ -283,6 +260,13 @@ int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock, 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; } @@ -296,7 +280,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; @@ -362,6 +346,14 @@ static int ext4_extent_find_extent(struct ext4_inode_ref *inode_ref, 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; @@ -424,13 +416,21 @@ 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); 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)) { @@ -466,13 +466,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 +488,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 +533,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 +575,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,14 +640,18 @@ 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; 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; @@ -694,6 +703,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 +729,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,13 +745,17 @@ 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; 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; @@ -797,6 +812,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, old_root->header); old_root->block.dirty = true; /* Re-initialize new root metadata */ @@ -813,6 +829,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 +846,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 +900,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 +927,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, 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 += @@ -928,6 +969,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, path_ptr->header); path_ptr->block.dirty = true; goto finish; @@ -938,8 +980,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 +1010,7 @@ append_extent: inode_ref->dirty = true; } + ext4_extent_block_csum_set(inode_ref, path_ptr->header); path_ptr->block.dirty = true; finish: @@ -990,6 +1037,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 /** * @}