Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f254e52c authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse
Browse files

Btrfs: verify csums on read

parent 75dfe396
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -109,14 +109,14 @@ static int comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2)
		return 1;
	if (k1.objectid < k2->objectid)
		return -1;
	if (k1.flags > k2->flags)
		return 1;
	if (k1.flags < k2->flags)
		return -1;
	if (k1.offset > k2->offset)
		return 1;
	if (k1.offset < k2->offset)
		return -1;
	if (k1.flags > k2->flags)
		return 1;
	if (k1.flags < k2->flags)
		return -1;
	return 0;
}

@@ -1165,7 +1165,6 @@ int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, struct btrfs_root
		BUG();
	ret = btrfs_search_slot(trans, root, cpu_key, path, data_size, 1);
	if (ret == 0) {
		btrfs_release_path(root, path);
		return -EEXIST;
	}
	if (ret < 0)
+32 −15
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ struct btrfs_transaction;
 */
#define BTRFS_NAME_LEN 255

/* 32 bytes in various csum fields */
#define BTRFS_CSUM_SIZE 32

/*
 * the key defines the order in the tree, and so it also defines (optimal)
 * block layout.  objectid corresonds to the inode number.  The flags
@@ -37,21 +40,21 @@ struct btrfs_transaction;
 */
struct btrfs_disk_key {
	__le64 objectid;
	__le32 flags;
	__le64 offset;
	__le32 flags;
} __attribute__ ((__packed__));

struct btrfs_key {
	u64 objectid;
	u32 flags;
	u64 offset;
	u32 flags;
} __attribute__ ((__packed__));

/*
 * every tree block (leaf or node) starts with this header.
 */
struct btrfs_header {
	__le32 csum[8];
	u8 csum[BTRFS_CSUM_SIZE];
	u8 fsid[16]; /* FS specific uuid */
	__le64 blocknr; /* which block this node is supposed to live in */
	__le64 generation;
@@ -75,7 +78,7 @@ struct buffer_head;
 * it currently lacks any block count etc etc
 */
struct btrfs_super_block {
	__le32 csum[8];
	u8 csum[BTRFS_CSUM_SIZE];
	/* the first 3 fields must match struct btrfs_header */
	u8 fsid[16];    /* FS specific uuid */
	__le64 blocknr; /* this block number */
@@ -147,7 +150,7 @@ struct btrfs_extent_item {
} __attribute__ ((__packed__));

struct btrfs_inode_timespec {
	__le32 sec;
	__le64 sec;
	__le32 nsec;
} __attribute__ ((__packed__));

@@ -214,6 +217,10 @@ struct btrfs_file_extent_item {
	__le64 num_blocks;
} __attribute__ ((__packed__));

struct btrfs_csum_item {
	u8 csum[BTRFS_CSUM_SIZE];
} __attribute__ ((__packed__));

struct btrfs_inode_map_item {
	struct btrfs_disk_key key;
} __attribute__ ((__packed__));
@@ -283,27 +290,32 @@ struct btrfs_root {
 * a (hopefully) huge chunk of disk
 */
#define BTRFS_EXTENT_DATA_KEY	4
/*
 * csum items have the checksums for data in the extents
 */
#define BTRFS_CSUM_ITEM_KEY	5

/*
 * root items point to tree roots.  There are typically in the root
 * tree used by the super block to find all the other trees
 */
#define BTRFS_ROOT_ITEM_KEY	5
#define BTRFS_ROOT_ITEM_KEY	6
/*
 * extent items are in the extent map tree.  These record which blocks
 * are used, and how many references there are to each block
 */
#define BTRFS_EXTENT_ITEM_KEY	6
#define BTRFS_EXTENT_ITEM_KEY	7

/*
 * the inode map records which inode numbers are in use and where
 * they actually live on disk
 */
#define BTRFS_INODE_MAP_ITEM_KEY 7
#define BTRFS_INODE_MAP_ITEM_KEY 8
/*
 * string items are for debugging.  They just store a short string of
 * data in the FS
 */
#define BTRFS_STRING_ITEM_KEY	8
#define BTRFS_STRING_ITEM_KEY	9

static inline u64 btrfs_inode_generation(struct btrfs_inode_item *i)
{
@@ -407,15 +419,15 @@ static inline void btrfs_set_inode_compat_flags(struct btrfs_inode_item *i,
	i->compat_flags = cpu_to_le16(val);
}

static inline u32 btrfs_timespec_sec(struct btrfs_inode_timespec *ts)
static inline u64 btrfs_timespec_sec(struct btrfs_inode_timespec *ts)
{
	return le32_to_cpu(ts->sec);
	return le64_to_cpu(ts->sec);
}

static inline void btrfs_set_timespec_sec(struct btrfs_inode_timespec *ts,
					  u32 val)
					  u64 val)
{
	ts->sec = cpu_to_le32(val);
	ts->sec = cpu_to_le64(val);
}

static inline u32 btrfs_timespec_nsec(struct btrfs_inode_timespec *ts)
@@ -429,8 +441,6 @@ static inline void btrfs_set_timespec_nsec(struct btrfs_inode_timespec *ts,
	ts->nsec = cpu_to_le32(val);
}



static inline u64 btrfs_extent_owner(struct btrfs_extent_item *ei)
{
	return le64_to_cpu(ei->owner);
@@ -905,4 +915,11 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root,
			     struct btrfs_path *path, u64 objectid,
			     u64 blocknr, int mod);
int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
			  struct btrfs_root *root,
			  u64 objectid, u64 offset,
			  char *data, size_t len);
int btrfs_csum_verify_file_block(struct btrfs_root *root,
				 u64 objectid, u64 offset,
				 char *data, size_t len);
#endif
+26 −11
Original line number Diff line number Diff line
@@ -127,31 +127,46 @@ static int btree_get_block(struct inode *inode, sector_t iblock,
	return 0;
}

static int csum_tree_block(struct btrfs_root * root, struct buffer_head *bh,
			    int verify)
int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len,
		    char *result)
{
	struct btrfs_node *node = btrfs_buffer_node(bh);
	struct scatterlist sg;
	struct crypto_hash *tfm = root->fs_info->hash_tfm;
	struct hash_desc desc;
	int ret;
	char result[32];

	desc.tfm = tfm;
	desc.flags = 0;
	sg_init_one(&sg, bh->b_data + 32, bh->b_size - 32);
	sg_init_one(&sg, data, len);
	spin_lock(&root->fs_info->hash_lock);
	ret = crypto_hash_digest(&desc, &sg, bh->b_size - 32, result);
	ret = crypto_hash_digest(&desc, &sg, len, result);
	spin_unlock(&root->fs_info->hash_lock);
	if (ret) {
		printk("sha256 digest failed\n");
	}
	return ret;
}
static int csum_tree_block(struct btrfs_root *root, struct buffer_head *bh,
			   int verify)
{
	char result[BTRFS_CSUM_SIZE];
	int ret;
	struct btrfs_node *node;

	ret = btrfs_csum_data(root, bh->b_data + BTRFS_CSUM_SIZE,
			      bh->b_size - BTRFS_CSUM_SIZE, result);
	if (ret)
		return ret;
	if (verify) {
		if (memcmp(node->header.csum, result, sizeof(result)))
			printk("csum verify failed on %Lu\n", bh->b_blocknr);
		return -EINVAL;
	} else
		memcpy(node->header.csum, result, sizeof(node->header.csum));
		if (memcmp(bh->b_data, result, BTRFS_CSUM_SIZE)) {
			printk("checksum verify failed on %lu\n",
			       bh->b_blocknr);
			return 1;
		}
	} else {
		node = btrfs_buffer_node(bh);
		memcpy(&node->header.csum, result, BTRFS_CSUM_SIZE);
	}
	return 0;
}

+2 −0
Original line number Diff line number Diff line
@@ -39,4 +39,6 @@ void btrfs_block_release(struct btrfs_root *root, struct buffer_head *buf);
int write_ctree_super(struct btrfs_trans_handle *trans,
		      struct btrfs_root *root);
struct buffer_head *btrfs_find_tree_block(struct btrfs_root *root, u64 blocknr);
int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len,
		    char *result);
#endif
+60 −0
Original line number Diff line number Diff line
@@ -57,3 +57,63 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
	ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
	return ret;
}

int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
			  struct btrfs_root *root,
			  u64 objectid, u64 offset,
			  char *data, size_t len)
{
	int ret;
	struct btrfs_key file_key;
	struct btrfs_path path;
	struct btrfs_csum_item *item;

	btrfs_init_path(&path);
	file_key.objectid = objectid;
	file_key.offset = offset;
	file_key.flags = 0;
	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
	ret = btrfs_insert_empty_item(trans, root, &path, &file_key,
				      BTRFS_CSUM_SIZE);
	if (ret != 0 && ret != -EEXIST)
		goto fail;
	item = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]), path.slots[0],
			      struct btrfs_csum_item);
	ret = 0;
	ret = btrfs_csum_data(root, data, len, item->csum);
	mark_buffer_dirty(path.nodes[0]);
fail:
	btrfs_release_path(root, &path);
	return ret;
}

int btrfs_csum_verify_file_block(struct btrfs_root *root,
				 u64 objectid, u64 offset,
				 char *data, size_t len)
{
	int ret;
	struct btrfs_key file_key;
	struct btrfs_path path;
	struct btrfs_csum_item *item;
	char result[BTRFS_CSUM_SIZE];

	btrfs_init_path(&path);
	file_key.objectid = objectid;
	file_key.offset = offset;
	file_key.flags = 0;
	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
	ret = btrfs_search_slot(NULL, root, &file_key, &path, 0, 0);
	if (ret)
		goto fail;
	item = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]), path.slots[0],
			      struct btrfs_csum_item);
	ret = 0;
	ret = btrfs_csum_data(root, data, len, result);
	WARN_ON(ret);
	if (memcmp(result, item->csum, BTRFS_CSUM_SIZE))
		ret = 1;
fail:
	btrfs_release_path(root, &path);
	return ret;
}
Loading