ext4_journal: journal write skeleton code.
[lwext4.git] / lwext4 / ext4_extent_full.c
index 060f292c8208628782ccd5fb02758d25856df105..2d34d67a6026f7d8a46582140e6536d2cfb3922e 100644 (file)
@@ -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);