ref: baef42af22f58e2b2668467a2efffbc868b470df
parent: 7bec846d340f30b82688e4fb1ca21e8d046fd233
author: ngkaho1234 <[email protected]>
date: Sun Oct 25 19:25:34 EDT 2015
METADATA_CSUM: Linear and HTree dir added.
--- a/lwext4/ext4_dir.c
+++ b/lwext4/ext4_dir.c
@@ -42,6 +42,7 @@
#include "ext4_config.h"
#include "ext4_dir.h"
#include "ext4_dir_idx.h"
+#include "ext4_crc32c.h"
#include "ext4_inode.h"
#include "ext4_fs.h"
@@ -49,6 +50,81 @@
/****************************************************************************/
+/* Walk through a dirent block to find a checksum "dirent" at the tail */
+static struct ext4_directory_entry_tail *
+ext4_dir_get_tail(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *de)
+{
+ struct ext4_directory_entry_tail *t;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+ t = EXT4_DIRENT_TAIL(de, ext4_sb_get_block_size(sb));
+
+ if (t->reserved_zero1 ||
+ to_le16(t->rec_len) != sizeof(struct ext4_directory_entry_tail) ||
+ t->reserved_zero2 ||
+ t->reserved_ft != EXT4_DIRENTRY_DIR_CSUM)
+ return NULL;
+
+ return t;
+}
+
+static uint32_t ext4_dir_checksum(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent, int size)
+{
+ uint32_t checksum;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+ /* First calculate crc32 checksum against fs uuid */
+ checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
+ /* Then calculate crc32 checksum against directory entries */
+ checksum = ext4_crc32c(checksum, dirent, size);
+ return checksum;
+}
+
+__unused int
+ext4_dir_checksum_verify(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent)
+{
+ struct ext4_directory_entry_tail *t;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+ /* Compute the checksum only if the filesystem supports it */
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ t = ext4_dir_get_tail(inode_ref, dirent);
+ if (!t) {
+ /* There is no space to hold the checksum */
+ return 0;
+ }
+
+ if (t->checksum != to_le32(ext4_dir_checksum(inode_ref, dirent,
+ (char *)t - (char *)dirent)))
+ return 0;
+
+ }
+ return 1;
+}
+
+void ext4_dir_set_checksum(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent)
+{
+ struct ext4_directory_entry_tail *t;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+ /* Compute the checksum only if the filesystem supports it */
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ t = ext4_dir_get_tail(inode_ref, dirent);
+ if (!t) {
+ /* There is no space to hold the checksum */
+ return;
+ }
+
+ t->checksum = to_le32(ext4_dir_checksum(inode_ref, dirent,
+ (char *)t - (char *)dirent));
+ }
+}
+
/**@brief Do some checks before returning iterator.
* @param it Iterator to be checked
* @param block_size Size of data block
@@ -285,7 +361,7 @@
return rc;
/* If adding is successful, function can finish */
- rc = ext4_dir_try_insert_entry(&fs->sb, &block, child, name,
+ rc = ext4_dir_try_insert_entry(&fs->sb, parent, &block, child, name,
name_len);
if (rc == EOK)
success = true;
@@ -320,6 +396,8 @@
name_len);
/* Save new block */
+ ext4_dir_set_checksum(parent,
+ (struct ext4_directory_entry_ll *)new_block.data);
new_block.dirty = true;
rc = ext4_block_set(fs->bdev, &new_block);
@@ -453,6 +531,8 @@
tmp_dentry, tmp_dentry_length + del_entry_length);
}
+ ext4_dir_set_checksum(parent,
+ (struct ext4_directory_entry_ll *)result.block.data);
result.block.dirty = true;
return ext4_dir_destroy_result(parent, &result);
@@ -459,6 +539,7 @@
}
int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
+ struct ext4_inode_ref *inode_ref,
struct ext4_block *target_block,
struct ext4_inode_ref *child, const char *name,
uint32_t name_len)
@@ -488,6 +569,9 @@
if ((inode == 0) && (rec_len >= required_len)) {
ext4_dir_write_entry(sb, dentry, rec_len, child, name,
name_len);
+ ext4_dir_set_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ target_block->data);
target_block->dirty = true;
return EOK;
@@ -517,6 +601,9 @@
ext4_dir_write_entry(sb, new_entry, free_space,
child, name, name_len);
+ ext4_dir_set_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ target_block->data);
target_block->dirty = true;
return EOK;
}
--- a/lwext4/ext4_dir.h
+++ b/lwext4/ext4_dir.h
@@ -229,6 +229,7 @@
/**@brief Try to insert entry to concrete data block.
* @param sb Superblock
+ * @param inode_ref Directory i-node
* @param target_block Block to try to insert entry to
* @param child Child i-node to be inserted by new entry
* @param name Name of the new entry
@@ -236,6 +237,7 @@
* @return Error code
*/
int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
+ struct ext4_inode_ref *inode_ref,
struct ext4_block *target_block,
struct ext4_inode_ref *child, const char *name,
uint32_t name_len);
@@ -260,6 +262,9 @@
*/
int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
struct ext4_directory_search_result *result);
+
+void ext4_dir_set_checksum(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent);
#endif /* EXT4_DIR_H_ */
--- a/lwext4/ext4_dir_idx.c
+++ b/lwext4/ext4_dir_idx.c
@@ -40,6 +40,7 @@
#include "ext4_blockdev.h"
#include "ext4_fs.h"
#include "ext4_super.h"
+#include "ext4_crc32c.h"
#include "ext4_hash.h"
#include <string.h>
@@ -202,6 +203,137 @@
&hinfo->hash, &hinfo->minor_hash);
}
+static uint32_t
+ext4_dir_dx_checksum(struct ext4_inode_ref *inode_ref,
+ void *dirent,
+ int count_offset, int count, struct ext4_directory_dx_tail *t)
+{
+ uint32_t orig_checksum, checksum = 0;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+ int size;
+
+ /* Compute the checksum only if the filesystem supports it */
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ size = count_offset +
+ (count * sizeof(struct ext4_directory_dx_tail));
+ orig_checksum = t->checksum;
+ t->checksum = 0;
+ /* First calculate crc32 checksum against fs uuid */
+ checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
+ /* Then calculate crc32 checksum against all the dx_entry */
+ checksum = ext4_crc32c(checksum, dirent, size);
+ /* Finally calculate crc32 checksum for dx_tail */
+ checksum = ext4_crc32c(checksum, t,
+ sizeof(struct ext4_directory_dx_tail));
+ t->checksum = orig_checksum;
+ }
+ return checksum;
+}
+
+static struct ext4_directory_dx_countlimit *
+ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent,
+ int *offset)
+{
+ struct ext4_directory_entry_ll *dp;
+ struct ext4_directory_dx_root *root;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+ int count_offset;
+
+ if (ext4_dir_entry_ll_get_entry_length(dirent) ==
+ ext4_sb_get_block_size(sb))
+ count_offset = 8;
+ else if (ext4_dir_entry_ll_get_entry_length(dirent) == 12) {
+ root = (struct ext4_directory_dx_root *)dirent;
+ dp = (struct ext4_directory_entry_ll *)&root->dots[1];
+ if (ext4_dir_entry_ll_get_entry_length(dp) !=
+ ext4_sb_get_block_size(sb) - 12)
+ return NULL;
+ if (root->info.reserved_zero ||
+ root->info.info_length != sizeof(struct ext4_directory_dx_root_info))
+ return NULL;
+ count_offset = 32;
+ } else
+ return NULL;
+
+ if (offset)
+ *offset = count_offset;
+ return (struct ext4_directory_dx_countlimit *)(((char *)dirent) + count_offset);
+}
+
+/*
+ * BIG FAT NOTES:
+ * Currently we do not verify the checksum of HTree node.
+ */
+__unused static int
+ext4_dir_dx_checksum_verify(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent)
+{
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+ int count_offset, limit, count;
+
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ struct ext4_directory_dx_countlimit *countlimit =
+ ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset);
+ if (!countlimit) {
+ /* Directory seems corrupted. */
+ return 1;
+ }
+ struct ext4_directory_dx_tail *t;
+ limit = ext4_dir_dx_countlimit_get_limit(countlimit);
+ count = ext4_dir_dx_countlimit_get_count(countlimit);
+ if (count_offset + (limit * sizeof(struct ext4_directory_dx_entry)) >
+ ext4_sb_get_block_size(sb) -
+ sizeof(struct ext4_directory_dx_tail)) {
+ /* There is no space to hold the checksum */
+ return 1;
+ }
+ t = (struct ext4_directory_dx_tail *)
+ (((struct ext4_directory_dx_entry *)countlimit) + limit);
+
+ if (t->checksum != to_le32(ext4_dir_dx_checksum(inode_ref,
+ dirent,
+ count_offset,
+ count, t)))
+ return 0;
+ }
+ return 1;
+}
+
+static void
+ext4_dir_set_dx_checksum(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_entry_ll *dirent)
+{
+ int count_offset, limit, count;
+ struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ struct ext4_directory_dx_countlimit *countlimit =
+ ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset);
+ if (!countlimit) {
+ /* Directory seems corrupted. */
+ return;
+ }
+ struct ext4_directory_dx_tail *t;
+ limit = ext4_dir_dx_countlimit_get_limit(countlimit);
+ count = ext4_dir_dx_countlimit_get_count(countlimit);
+ if (count_offset + (limit * sizeof(struct ext4_directory_dx_entry)) >
+ ext4_sb_get_block_size(sb) -
+ sizeof(struct ext4_directory_dx_tail)) {
+ /* There is no space to hold the checksum */
+ return;
+ }
+ t = (struct ext4_directory_dx_tail *)
+ (((struct ext4_directory_dx_entry *)countlimit) + limit);
+
+ t->checksum =
+ to_le32(ext4_dir_dx_checksum(inode_ref, dirent, count_offset, count, t));
+ }
+}
+
/****************************************************************************/
int ext4_dir_dx_init(struct ext4_inode_ref *dir)
@@ -208,6 +340,7 @@
{
/* Load block 0, where will be index root located */
ext4_fsblk_t fblock;
+ struct ext4_sblock *sb = &dir->fs->sb;
int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false);
if (rc != EOK)
return rc;
@@ -261,7 +394,22 @@
/* Fill the whole block with empty entry */
struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;
- ext4_dir_entry_ll_set_entry_length(block_entry, block_size);
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ ext4_dir_entry_ll_set_entry_length(block_entry,
+ sizeof(struct ext4_directory_entry_tail));
+ ext4_dir_entry_ll_set_name_length(sb,
+ block_entry,
+ 0);
+ ext4_dir_entry_ll_set_inode_type(sb,
+ block_entry,
+ EXT4_DIRENTRY_DIR_CSUM);
+ ext4_dir_set_checksum(dir,
+ (struct ext4_directory_entry_ll *)new_block.data);
+ } else {
+ ext4_dir_entry_ll_set_entry_length(block_entry, block_size);
+ }
+
ext4_dir_entry_ll_set_inode(block_entry, 0);
new_block.dirty = true;
@@ -275,6 +423,8 @@
struct ext4_directory_dx_entry *entry = root->entries;
ext4_dir_dx_entry_set_block(entry, iblock);
+ ext4_dir_set_dx_checksum(dir,
+ (struct ext4_directory_entry_ll *)block.data);
block.dirty = true;
return ext4_block_set(dir->fs->bdev, &block);
@@ -681,6 +831,7 @@
/**@brief Insert new index entry to block.
* Note that space for new entry must be checked by caller.
+ * @param inode_ref Directory i-node
* @param index_block Block where to insert new entry
* @param hash Hash value covered by child node
* @param iblock Logical number of child block
@@ -687,7 +838,8 @@
*
*/
static void
-ext4_dir_dx_insert_entry(struct ext4_directory_dx_block *index_block,
+ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_dx_block *index_block,
uint32_t hash, uint32_t iblock)
{
struct ext4_directory_dx_entry *old_index_entry = index_block->position;
@@ -708,6 +860,8 @@
ext4_dir_dx_countlimit_set_count(countlimit, count + 1);
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)index_block->block.data);
index_block->block.dirty = true;
}
@@ -878,6 +1032,10 @@
}
/* Do some steps to finish operation */
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)old_data_block->data);
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)new_data_block_tmp.data);
old_data_block->dirty = true;
new_data_block_tmp.dirty = true;
@@ -884,7 +1042,9 @@
free(sort_array);
free(entry_buffer);
- ext4_dir_dx_insert_entry(index_block, new_hash + continued, new_iblock);
+ ext4_dir_dx_insert_entry(inode_ref,
+ index_block,
+ new_hash + continued, new_iblock);
*new_data_block = new_data_block_tmp;
@@ -1003,6 +1163,10 @@
uint32_t position_index =
(dx_block->position - dx_block->entries);
if (position_index >= count_left) {
+ ext4_dir_set_dx_checksum(
+ inode_ref,
+ (struct ext4_directory_entry_ll *)
+ dx_block->block.data);
dx_block->block.dirty = true;
struct ext4_block block_tmp = dx_block->block;
@@ -1017,11 +1181,21 @@
}
/* Finally insert new entry */
- ext4_dir_dx_insert_entry(dx_blocks, hash_right,
+ ext4_dir_dx_insert_entry(inode_ref,
+ dx_blocks, hash_right,
new_iblock);
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ dx_blocks[0].block.data);
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ dx_blocks[1].block.data);
dx_blocks[0].block.dirty = true;
dx_blocks[1].block.dirty = true;
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ new_block.data);
new_block.dirty = true;
return ext4_block_set(inode_ref->fs->bdev, &new_block);
} else {
@@ -1066,6 +1240,12 @@
*new_dx_block = dx_block;
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ dx_blocks[0].block.data);
+ ext4_dir_set_dx_checksum(inode_ref,
+ (struct ext4_directory_entry_ll *)
+ dx_blocks[1].block.data);
dx_blocks[0].block.dirty = true;
dx_blocks[1].block.dirty = true;
}
@@ -1143,7 +1323,7 @@
goto release_index;
/* Check if insert operation passed */
- rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child, name,
+ rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, name,
name_len);
if (rc == EOK)
goto release_target_index;
@@ -1161,10 +1341,10 @@
uint32_t new_block_hash =
ext4_dir_dx_entry_get_hash(dx_block->position + 1);
if (hinfo.hash >= new_block_hash)
- rc = ext4_dir_try_insert_entry(&fs->sb, &new_block, child, name,
+ rc = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block, child, name,
name_len);
else
- rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child,
+ rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child,
name, name_len);
/* Cleanup */
@@ -1218,6 +1398,9 @@
/* Fill the inode field with a new parent ino. */
ext4_dx_dot_entry_set_inode(&root->dots[1], parent_inode);
+ ext4_dir_set_dx_checksum(dir,
+ (struct ext4_directory_entry_ll *)
+ block.data);
block.dirty = true;
return ext4_block_set(dir->fs->bdev, &block);
--- a/lwext4/ext4_types.h
+++ b/lwext4/ext4_types.h
@@ -480,6 +480,8 @@
EXT4_DIRENTRY_SOCK,
EXT4_DIRENTRY_SYMLINK };
+#define EXT4_DIRENTRY_DIR_CSUM 0xDE
+
union ext4_directory_entry_ll_internal {
uint8_t name_length_high; /* Higher 8 bits of name length */
uint8_t inode_type; /* Type of referenced inode (in rev >= 0.5) */
@@ -561,6 +563,31 @@
struct ext4_directory_dx_entry *entries;
struct ext4_directory_dx_entry *position;
};
+
+/*
+ * This goes at the end of each htree block.
+ */
+struct ext4_directory_dx_tail {
+ uint32_t reserved;
+ uint32_t checksum; /* crc32c(uuid+inum+dirblock) */
+};
+
+/*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext4_directory_entry_tail {
+ uint32_t reserved_zero1; /* Pretend to be unused */
+ uint16_t rec_len; /* 12 */
+ uint8_t reserved_zero2; /* Zero name length */
+ uint8_t reserved_ft; /* 0xDE, fake file type */
+ uint32_t checksum; /* crc32c(uuid+inum+dirblock) */
+};
+
+#define EXT4_DIRENT_TAIL(block, blocksize) \
+ ((struct ext4_directory_entry_tail *)(((char *)(block)) + \
+ ((blocksize) - \
+ sizeof(struct ext4_directory_entry_tail))))
#define EXT4_ERR_BAD_DX_DIR (-25000)