#include "ext4_blockdev.h"
#include "ext4_fs.h"
#include "ext4_super.h"
+#include "ext4_crc32c.h"
#include "ext4_balloc.h"
#include "ext4_debug.h"
*/
static void ext4_ext_store_pblock(struct ext4_extent *ex, ext4_fsblk_t pb)
{
- ex->start_lo = to_le32((unsigned long)(pb & 0xffffffff));
- ex->start_hi = to_le16((unsigned long)((pb >> 31) >> 1) & 0xffff);
+ ex->start_lo = to_le32((uint32_t)(pb & 0xffffffff));
+ ex->start_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff);
}
/*
*/
static void ext4_idx_store_pblock(struct ext4_extent_index *ix, ext4_fsblk_t pb)
{
- ix->leaf_lo = to_le32((unsigned long)(pb & 0xffffffff));
- ix->leaf_hi = to_le16((unsigned long)((pb >> 31) >> 1) & 0xffff);
+ ix->leaf_lo = to_le32((uint32_t)(pb & 0xffffffff));
+ ix->leaf_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff);
}
static int ext4_allocate_single_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;
}
size = (block_size - sizeof(struct ext4_extent_header)) /
sizeof(struct ext4_extent_index);
+#ifdef AGGRESSIVE_TEST
+ if (size > 5)
+ size = 5;
+#endif
return size;
}
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;
}
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;
}
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;
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.
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;
}
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;
path->extent = l - 1;
}
-#define EXT4_EXT_PATH_INC_DEPTH 1
-
static int ext4_find_extent(struct ext4_inode_ref *inode_ref, ext4_lblk_t block,
struct ext4_extent_path **orig_path, uint32_t flags)
{
}
}
if (!path) {
- int32_t path_depth = depth + EXT4_EXT_PATH_INC_DEPTH;
+ int32_t path_depth = depth + 1;
/* account possible depth increase */
path = calloc(1, sizeof(struct ext4_extent_path) *
(path_depth + 1));
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;
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;
};
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;
int err;
struct ext4_extent_header *eh;
+ *need_grow = false;
+
if (curp->index && insert_index == to_le32(curp->index->first_block))
return EIO;
ix = EXT_LAST_INDEX(eh);
}
} else {
- err = EXT_INODE_HDR_NEED_GROW;
+ err = EOK;
+ *need_grow = true;
goto out;
}
} else {
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);
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) >
} 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))
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) >
} 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))
}
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;
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;
ex = EXT_LAST_EXTENT(eh);
}
} else {
- err = EXT_INODE_HDR_NEED_GROW;
+ err = EOK;
+ *need_grow = true;
goto out;
}
} else {
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);
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;
}
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);
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,
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;
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;
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;
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);
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);
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);
}
* 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,
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) {
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,
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;
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;
* 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);