ext4_journal: journal write skeleton code.
[lwext4.git] / lwext4 / ext4_extent_full.c
index 692eb12e71d5900a89deba1b8d6e2c9d8026ff65..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"
 
@@ -308,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;
 
@@ -333,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.
@@ -367,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;
 
@@ -388,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;
@@ -595,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;
 
@@ -765,7 +797,8 @@ out:
                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);
@@ -1036,7 +1069,8 @@ out:
                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);
@@ -1095,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;
@@ -1132,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);
 
@@ -1348,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);
@@ -1371,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);
                }
@@ -1438,6 +1476,34 @@ int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
                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) {
@@ -1657,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;