X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lwext4%2Fext4_extent_full.c;h=2d34d67a6026f7d8a46582140e6536d2cfb3922e;hb=4b25da872742a15a49343f758e16d5e0447d1c48;hp=060f292c8208628782ccd5fb02758d25856df105;hpb=75f7046dda4d04efbe74d0abeda61eff25686545;p=lwext4.git diff --git a/lwext4/ext4_extent_full.c b/lwext4/ext4_extent_full.c index 060f292..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; @@ -644,8 +692,6 @@ static ext4_lblk_t ext4_ext_block_index(struct ext4_extent_header *eh) return to_le32(EXT_FIRST_EXTENT(eh)->first_block); } -#define EXT_INODE_HDR_NEED_GROW 0x1 - struct ext_split_trans { ext4_fsblk_t ptr; struct ext4_extent_path path; @@ -653,11 +699,13 @@ struct ext_split_trans { }; static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref, - struct ext4_extent_path *path, int32_t at, + struct ext4_extent_path *path, + int32_t at, struct ext4_extent *newext, ext4_lblk_t insert_index, ext4_fsblk_t insert_block, - struct ext_split_trans *spt) + struct ext_split_trans *spt, + bool *need_grow) { struct ext4_extent_index *ix; struct ext4_extent_path *curp = path + at; @@ -666,6 +714,8 @@ static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref, int err; struct ext4_extent_header *eh; + *need_grow = false; + if (curp->index && insert_index == to_le32(curp->index->first_block)) return EIO; @@ -697,7 +747,8 @@ static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref, ix = EXT_LAST_INDEX(eh); } } else { - err = EXT_INODE_HDR_NEED_GROW; + err = EOK; + *need_grow = true; goto out; } } else { @@ -739,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); @@ -838,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) > @@ -846,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)) @@ -861,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) > @@ -869,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)) @@ -878,9 +940,12 @@ static bool ext4_ext_can_append(struct ext4_extent *ex1, } static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref, - struct ext4_extent_path *path, int32_t at, + struct ext4_extent_path *path, + int32_t at, struct ext4_extent *newext, - struct ext_split_trans *spt, uint32_t flags) + struct ext_split_trans *spt, + uint32_t flags, + bool *need_grow) { struct ext4_extent_path *curp = path + at; struct ext4_extent *ex = curp->extent; @@ -890,6 +955,8 @@ static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref, int unwritten; struct ext4_extent_header *eh = NULL; + *need_grow = false; + if (curp->extent && to_le32(newext->first_block) == to_le32(curp->extent->first_block)) return EIO; @@ -947,7 +1014,8 @@ static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref, ex = EXT_LAST_EXTENT(eh); } } else { - err = EXT_INODE_HDR_NEED_GROW; + err = EOK; + *need_grow = true; goto out; } } else { @@ -994,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); @@ -1060,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; @@ -1097,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); @@ -1131,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, @@ -1147,6 +1216,7 @@ static int ext4_ext_insert_extent(struct ext4_inode_ref *inode_ref, int32_t i, depth, level; int ret = EOK; ext4_fsblk_t ptr = 0; + bool need_grow = false; struct ext4_extent_path *path = *ppath; struct ext_split_trans *spt = NULL; struct ext_split_trans newblock; @@ -1172,18 +1242,21 @@ again: do { if (!i) { ret = ext4_ext_insert_leaf(inode_ref, path, depth - i, - newext, &newblock, flags); + newext, &newblock, flags, + &need_grow); } else { ret = ext4_ext_insert_index( inode_ref, path, depth - i, newext, ext4_ext_block_index( ext_block_hdr(&spt[i - 1].path.block)), - spt[i - 1].ptr, &newblock); + spt[i - 1].ptr, &newblock, + &need_grow); } ptr = newblock.ptr; - if (ret && ret != EXT_INODE_HDR_NEED_GROW) + if (ret != EOK) goto out; + else if (spt && ptr && !ret) { /* Prepare for the next iteration after splitting. */ spt[i] = newblock; @@ -1192,7 +1265,7 @@ again: i++; } while (ptr != 0 && i <= depth); - if (ret == EXT_INODE_HDR_NEED_GROW) { + if (need_grow) { ret = ext4_ext_grow_indepth(inode_ref, 0); if (ret) goto out; @@ -1221,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); @@ -1308,20 +1381,24 @@ static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref, to_le32(ex->first_block) <= to) { int32_t new_len = 0; int unwritten; - ext4_fsblk_t start, new_start; + 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) { - start = from; len -= from - start; new_len = from - start; + start = from; start_ex++; - } - if (start + len - 1 > to) { - len -= start + len - 1 - to; - new_len = start + len - 1 - to; - new_start += to + 1; - ex2 = 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; + } } ext4_ext_remove_blocks(inode_ref, ex, start, start + len - 1); @@ -1331,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); } @@ -1353,22 +1431,24 @@ 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; } -static int ext4_ext_more_to_rm(struct ext4_extent_path *path, ext4_lblk_t to) +static bool ext4_ext_more_to_rm(struct ext4_extent_path *path, ext4_lblk_t to) { if (!to_le16(path->header->entries_count)) - return 0; + return false; if (path->index > EXT_LAST_INDEX(path->header)) - return 0; + return false; if (to_le32(path->index->first_block) > to) - return 0; + return false; - return 1; + return true; } int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from, @@ -1383,13 +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 || - !IN_RANGE(from, to_le32(path[depth].extent->first_block), - ext4_ext_get_actual_len(path[depth].extent))) { + 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 (!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) { @@ -1443,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, @@ -1566,15 +1684,6 @@ static int ext4_ext_convert_to_initialized(struct ext4_inode_ref *inode_ref, return err; } -/* - * ext4_ext_next_allocated_block: - * returns allocated block in subsequent extent or EXT_MAX_BLOCKS. - * NOTE: it considers block number from index entry as - * allocated block. Thus, index entries have to be consistent - * with leaves. - */ -#define EXT_MAX_BLOCKS (ext4_lblk_t) - 1 - static ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_extent_path *path) { int32_t depth; @@ -1614,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; @@ -1659,7 +1768,8 @@ int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_fsblk_t iblock * this situations is possible, though, _during_ tree modification * this is why assert can't be put in ext4_ext_find_extent() */ - if ((ex = path[depth].extent)) { + ex = path[depth].extent; + if (ex) { ext4_lblk_t ee_block = to_le32(ex->first_block); ext4_fsblk_t ee_start = ext4_ext_pblock(ex); uint16_t ee_len = ext4_ext_get_actual_len(ex);