X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=lwext4%2Fext4_extent_full.c;h=2d34d67a6026f7d8a46582140e6536d2cfb3922e;hb=4b25da872742a15a49343f758e16d5e0447d1c48;hp=a7be463ad9a75ae3b01fd701646aa71be002e7f7;hpb=698ceba63af13eaa4b4a29f7b03edefc8cc251db;p=lwext4.git diff --git a/lwext4/ext4_extent_full.c b/lwext4/ext4_extent_full.c index a7be463..2d34d67 100644 --- a/lwext4/ext4_extent_full.c +++ b/lwext4/ext4_extent_full.c @@ -30,6 +30,7 @@ #include "ext4_blockdev.h" #include "ext4_fs.h" #include "ext4_super.h" +#include "ext4_crc32c.h" #include "ext4_balloc.h" #include "ext4_debug.h" @@ -178,6 +179,10 @@ static size_t ext4_ext_space_block(struct ext4_inode_ref *inode_ref) size = (block_size - sizeof(struct ext4_extent_header)) / sizeof(struct ext4_extent); +#ifdef AGGRESSIVE_TEST + if (size > 6) + size = 6; +#endif return size; } @@ -188,6 +193,10 @@ static size_t ext4_ext_space_block_idx(struct ext4_inode_ref *inode_ref) size = (block_size - sizeof(struct ext4_extent_header)) / sizeof(struct ext4_extent_index); +#ifdef AGGRESSIVE_TEST + if (size > 5) + size = 5; +#endif return size; } @@ -198,6 +207,10 @@ static size_t ext4_ext_space_root(struct ext4_inode_ref *inode_ref) size = sizeof(inode_ref->inode->blocks); size -= sizeof(struct ext4_extent_header); size /= sizeof(struct ext4_extent); +#ifdef AGGRESSIVE_TEST + if (size > 3) + size = 3; +#endif return size; } @@ -208,6 +221,10 @@ static size_t ext4_ext_space_root_idx(struct ext4_inode_ref *inode_ref) size = sizeof(inode_ref->inode->blocks); size -= sizeof(struct ext4_extent_header); size /= sizeof(struct ext4_extent_index); +#ifdef AGGRESSIVE_TEST + if (size > 4) + size = 4; +#endif return size; } @@ -292,11 +309,51 @@ static ext4_fsblk_t ext4_ext_new_meta_block(struct ext4_inode_ref *inode_ref, return newblock; } +#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 __unused, + 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)); +} + static int ext4_ext_dirty(struct ext4_inode_ref *inode_ref, struct ext4_extent_path *path) { if (path->block.lb_id) - path->block.dirty = true; + ext4_bcache_set_dirty(path->block.buf); else inode_ref->dirty = true; @@ -317,31 +374,15 @@ static void ext4_ext_drop_refs(struct ext4_inode_ref *inode_ref, for (i = 0; i <= depth; i++, path++) { if (path->block.lb_id) { + if (ext4_bcache_test_flag(path->block.buf, BC_DIRTY)) + ext4_extent_block_csum_set(inode_ref, + path->header); + ext4_block_set(inode_ref->fs->bdev, &path->block); } } } -/* - * Temporarily we don't need to support checksum. - */ -static uint32_t ext4_ext_block_csum(struct ext4_inode_ref *inode_ref __unused, - struct ext4_extent_header *eh __unused) -{ - /*TODO: should we add crc32 here ?*/ - /*return ext4_crc32c(inode->i_csum, eh, EXT4_EXTENT_TAIL_OFFSET(eh));*/ - return 0; -} - -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 = ext4_ext_block_csum(inode_ref, eh); -} - /* * Check that whether the basic information inside the extent header * is correct or not. @@ -351,6 +392,7 @@ static int ext4_ext_check(struct ext4_inode_ref *inode_ref, ext4_fsblk_t pblk __unused) { struct ext4_extent_tail *tail; + struct ext4_sblock *sb = &inode_ref->fs->sb; const char *error_msg; (void)error_msg; @@ -372,8 +414,14 @@ static int ext4_ext_check(struct ext4_inode_ref *inode_ref, } tail = find_ext4_extent_tail(eh); - if (tail->et_checksum != ext4_ext_block_csum(inode_ref, eh)) { - /* FIXME: Warning: extent checksum damaged? */ + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + if (tail->et_checksum != to_le32(ext4_ext_block_csum(inode_ref, eh))) { + ext4_dbg(DEBUG_EXTENT, + DBG_WARN "Extent block checksum failed." + "Blocknr: %" PRIu64"\n", + pblk); + + } } return EOK; @@ -579,7 +627,7 @@ static int ext4_ext_split_node(struct ext4_inode_ref *inode_ref, goto cleanup; /* For write access.# */ - ret = ext4_block_get(inode_ref->fs->bdev, &bh, newblock); + ret = ext4_block_get_noread(inode_ref->fs->bdev, &bh, newblock); if (ret != EOK) goto cleanup; @@ -742,14 +790,15 @@ static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref, err = EOK; out: - if (err != EOK) { + if (err != EOK || *need_grow) { if (bh.lb_id) ext4_block_set(inode_ref->fs->bdev, &bh); spt->ptr = 0; } else if (bh.lb_id) { /* If we got a sibling leaf. */ - bh.dirty = true; + ext4_extent_block_csum_set(inode_ref, ext_block_hdr(&bh)); + ext4_bcache_set_dirty(bh.buf); spt->path.p_block = ext4_idx_pblock(ix); spt->path.depth = to_le16(eh->depth); @@ -841,6 +890,10 @@ static bool ext4_ext_can_prepend(struct ext4_extent *ex1, ext4_ext_pblock(ex1)) return false; +#ifdef AGGRESSIVE_TEST + if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4) + return 0; +#else if (ext4_ext_is_unwritten(ex1)) { if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > @@ -849,6 +902,7 @@ static bool ext4_ext_can_prepend(struct ext4_extent *ex1, } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_INIT_MAX_LEN) return false; +#endif if (to_le32(ex2->first_block) + ext4_ext_get_actual_len(ex2) != to_le32(ex1->first_block)) @@ -864,6 +918,10 @@ static bool ext4_ext_can_append(struct ext4_extent *ex1, ext4_ext_pblock(ex2)) return false; +#ifdef AGGRESSIVE_TEST + if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4) + return 0; +#else if (ext4_ext_is_unwritten(ex1)) { if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > @@ -872,6 +930,7 @@ static bool ext4_ext_can_append(struct ext4_extent *ex1, } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_INIT_MAX_LEN) return false; +#endif if (to_le32(ex1->first_block) + ext4_ext_get_actual_len(ex1) != to_le32(ex2->first_block)) @@ -1003,14 +1062,15 @@ static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref, err = EOK; out: - if (err != EOK) { + if (err != EOK || *need_grow) { if (bh.lb_id) ext4_block_set(inode_ref->fs->bdev, &bh); spt->ptr = 0; } else if (bh.lb_id) { /* If we got a sibling leaf. */ - bh.dirty = true; + ext4_extent_block_csum_set(inode_ref, ext_block_hdr(&bh)); + ext4_bcache_set_dirty(bh.buf); spt->path.p_block = ext4_ext_pblock(ex); spt->path.depth = to_le16(eh->depth); @@ -1069,7 +1129,7 @@ static int ext4_ext_grow_indepth(struct ext4_inode_ref *inode_ref, return err; /* # */ - err = ext4_block_get(inode_ref->fs->bdev, &bh, newblock); + err = ext4_block_get_noread(inode_ref->fs->bdev, &bh, newblock); if (err != EOK) { ext4_ext_free_blocks(inode_ref, newblock, 1, 0); return err; @@ -1106,7 +1166,7 @@ static int ext4_ext_grow_indepth(struct ext4_inode_ref *inode_ref, } neh->depth = to_le16(to_le16(neh->depth) + 1); - bh.dirty = true; + ext4_bcache_set_dirty(bh.buf); inode_ref->dirty = true; ext4_block_set(inode_ref->fs->bdev, &bh); @@ -1140,13 +1200,13 @@ __unused static void print_path(struct ext4_extent_path *path) static void ext4_ext_replace_path(struct ext4_inode_ref *inode_ref, struct ext4_extent_path *path, - struct ext_split_trans *spt, int32_t depth, + struct ext_split_trans *spt, int32_t level) { + int32_t depth = ext_depth(inode_ref->inode); int32_t i = depth - level; - ext4_ext_drop_refs(inode_ref, path + i, 1); - path[i] = spt->path; + path[i] = spt[level].path; } static int ext4_ext_insert_extent(struct ext4_inode_ref *inode_ref, @@ -1234,7 +1294,7 @@ out: while (--level >= 0 && spt) { if (spt[level].switch_to) ext4_ext_replace_path(inode_ref, path, spt, - depth, level); + level); else if (spt[level].ptr) ext4_ext_drop_refs(inode_ref, &spt[level].path, 1); @@ -1322,21 +1382,24 @@ static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref, int32_t new_len = 0; int unwritten; ext4_lblk_t start, new_start; + ext4_fsblk_t newblock; new_start = start = to_le32(ex->first_block); len = ext4_ext_get_actual_len(ex); + newblock = ext4_ext_pblock(ex); if (start < from) { len -= from - start; new_len = from - start; start = from; start_ex++; + } else { + if (start + len - 1 > to) { + len -= start + len - 1 - to; + new_len = start + len - 1 - to; + new_start = to + 1; + newblock += to + 1 - start; + ex2 = ex; + } } - /* TODO: More complicated truncate operation. */ - /*if (start + len - 1 > to) {*/ - /*len -= start + len - 1 - to;*/ - /*new_len = start + len - 1 - to;*/ - /*new_start = to + 1;*/ - /*ex2 = ex;*/ - /*}*/ ext4_ext_remove_blocks(inode_ref, ex, start, start + len - 1); ex->first_block = to_le32(new_start); @@ -1345,6 +1408,7 @@ static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref, else { unwritten = ext4_ext_is_unwritten(ex); ex->block_count = to_le16(new_len); + ext4_ext_store_pblock(ex, newblock); if (unwritten) ext4_ext_mark_unwritten(ex); } @@ -1367,6 +1431,8 @@ static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref, * remove it from index block above */ if (err == EOK && eh->entries_count == 0 && path[depth].block.lb_id) err = ext4_ext_remove_idx(inode_ref, path, depth - 1); + else if (depth > 0) + path[depth - 1].index++; return err; } @@ -1376,7 +1442,7 @@ static bool ext4_ext_more_to_rm(struct ext4_extent_path *path, ext4_lblk_t to) if (!to_le16(path->header->entries_count)) return false; - if (path->index >= EXT_LAST_INDEX(path->header)) + if (path->index > EXT_LAST_INDEX(path->header)) return false; if (to_le32(path->index->first_block) > to) @@ -1397,14 +1463,47 @@ int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from, if (ret) goto out; + if (!path[depth].extent) { + ret = EOK; + goto out; + } + bool in_range = IN_RANGE(from, to_le32(path[depth].extent->first_block), ext4_ext_get_actual_len(path[depth].extent)); - if (!path[depth].extent || !in_range) { + if (!in_range) { ret = EOK; goto out; } + /* If we do remove_space inside the range of an extent */ + if ((to_le32(path[depth].extent->first_block) < from) && + (to < to_le32(path[depth].extent->first_block) + + ext4_ext_get_actual_len(path[depth].extent) - 1)) { + + struct ext4_extent *ex = path[depth].extent, newex; + int unwritten = ext4_ext_is_unwritten(ex); + ext4_lblk_t ee_block = to_le32(ex->first_block); + int32_t len = ext4_ext_get_actual_len(ex); + ext4_fsblk_t newblock = + to + 1 - ee_block + ext4_ext_pblock(ex); + + ex->block_count = to_le16(from - ee_block); + if (unwritten) + ext4_ext_mark_unwritten(ex); + + ext4_ext_dirty(inode_ref, path + depth); + + newex.first_block = to_le32(to + 1); + newex.block_count = to_le16(ee_block + len - 1 - to); + ext4_ext_store_pblock(&newex, newblock); + if (unwritten) + ext4_ext_mark_unwritten(&newex); + + ret = ext4_ext_insert_extent(inode_ref, &path, &newex, 0); + goto out; + } + i = depth; while (i >= 0) { if (i == depth) { @@ -1458,10 +1557,14 @@ int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from, i++; } else { - if (!eh->entries_count && i > 0) - ret = ext4_ext_remove_idx(inode_ref, path, - i - 1); + if (i > 0) { + if (!eh->entries_count) + ret = ext4_ext_remove_idx(inode_ref, path, + i - 1); + else + path[i - 1].index++; + } if (i) ext4_block_set(inode_ref->fs->bdev, @@ -1620,12 +1723,12 @@ static int ext4_ext_zero_unwritten_range(struct ext4_inode_ref *inode_ref, uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); for (i = 0; i < blocks_count; i++) { struct ext4_block bh = EXT4_BLOCK_ZERO(); - err = ext4_block_get(inode_ref->fs->bdev, &bh, block + i); + err = ext4_block_get_noread(inode_ref->fs->bdev, &bh, block + i); if (err != EOK) break; memset(bh.data, 0, block_size); - bh.dirty = true; + ext4_bcache_set_dirty(bh.buf); err = ext4_block_set(inode_ref->fs->bdev, &bh); if (err != EOK) break;