mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 12:10:38 -04:00
Merge tag 'for-7.2-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba: - fix root structure leak after relocation error - fix optimization when checksums are read from commit root, fall back to checksum root during relocation - in tree-checker, validate length of inode reference in items - validate properties before setting them - validate free space cache entries on load - transaction abort fixes - fix printing of internal trees as signed numbers - add error messages after critical lzo compression errors * tag 'for-7.2-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux: btrfs: print-tree: print header owner as signed btrfs: decentralize transaction aborts in create_reloc_root() btrfs: tree-checker: validate INODE_REF's namelen btrfs: lzo: add error message for invalid headers btrfs: fallback to transaction csum tree on a commit root csum miss btrfs: fix root leak if its reloc root is unexpected in merge_reloc_roots() btrfs: reject free space cache with more entries than pages btrfs: fix transaction abort logic in btrfs_fileattr_set() btrfs: validate properties before setting them
This commit is contained in:
@@ -358,6 +358,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
|
||||
const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits;
|
||||
int ret = 0;
|
||||
u32 bio_offset = 0;
|
||||
bool using_commit_root = false;
|
||||
|
||||
if ((inode->flags & BTRFS_INODE_NODATASUM) ||
|
||||
test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state))
|
||||
@@ -431,6 +432,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
|
||||
* from across transactions.
|
||||
*/
|
||||
if (bbio->csum_search_commit_root) {
|
||||
using_commit_root = true;
|
||||
path->search_commit_root = true;
|
||||
path->skip_locking = true;
|
||||
down_read(&fs_info->commit_root_sem);
|
||||
@@ -463,6 +465,28 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
|
||||
* assume this is the case.
|
||||
*/
|
||||
if (count == 0) {
|
||||
/*
|
||||
* If an extent is relocated in the current transaction
|
||||
* then relocation writes a new csum without updating
|
||||
* the extent map generation. Until the next commit, we
|
||||
* will see a hole in that case, so we need to fallback
|
||||
* to searching the transaction csum root.
|
||||
*
|
||||
* Note that a commit root lookup of a referenced extent can
|
||||
* only miss, not return a stale csum. A freed extent's csum
|
||||
* is deleted in the same transaction and its bytenr is not
|
||||
* reusable until that transaction has committed and the
|
||||
* extent is unpinned.
|
||||
*/
|
||||
if (using_commit_root) {
|
||||
up_read(&fs_info->commit_root_sem);
|
||||
using_commit_root = false;
|
||||
path->search_commit_root = false;
|
||||
path->skip_locking = false;
|
||||
btrfs_release_path(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(csum_dst, 0, csum_size);
|
||||
count = 1;
|
||||
|
||||
@@ -481,7 +505,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
|
||||
bio_offset += count * sectorsize;
|
||||
}
|
||||
|
||||
if (bbio->csum_search_commit_root)
|
||||
if (using_commit_root)
|
||||
up_read(&fs_info->commit_root_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -551,6 +551,9 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index)
|
||||
u32 crc = ~(u32)0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (index >= io_ctl->num_pages)
|
||||
return -EIO;
|
||||
|
||||
if (index == 0)
|
||||
offset = sizeof(u32) * io_ctl->num_pages;
|
||||
|
||||
|
||||
@@ -289,6 +289,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
|
||||
int ret;
|
||||
const char *comp = NULL;
|
||||
u32 inode_flags;
|
||||
bool prop_set = false;
|
||||
|
||||
if (btrfs_root_readonly(root))
|
||||
return -EROFS;
|
||||
@@ -401,16 +402,15 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
|
||||
if (comp) {
|
||||
ret = btrfs_set_prop(trans, inode, "btrfs.compression",
|
||||
comp, strlen(comp), 0);
|
||||
if (unlikely(ret)) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
if (ret)
|
||||
goto out_end_trans;
|
||||
}
|
||||
prop_set = true;
|
||||
} else {
|
||||
ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL, 0, 0);
|
||||
if (unlikely(ret && ret != -ENODATA)) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
prop_set = (ret == 0);
|
||||
/* If ret == -ENODATA ignore and proceed to update inode item. */
|
||||
if (ret && ret != -ENODATA)
|
||||
goto out_end_trans;
|
||||
}
|
||||
}
|
||||
|
||||
update_flags:
|
||||
@@ -420,6 +420,12 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
|
||||
inode_inc_iversion(&inode->vfs_inode);
|
||||
inode_set_ctime_current(&inode->vfs_inode);
|
||||
ret = btrfs_update_inode(trans, inode);
|
||||
/*
|
||||
* If we set a property or deleted one, we must abort if we fail to
|
||||
* update the inode, to avoid persisting an inconsistent state.
|
||||
*/
|
||||
if (unlikely(ret && prop_set))
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
|
||||
out_end_trans:
|
||||
btrfs_end_transaction(trans);
|
||||
|
||||
@@ -552,17 +552,26 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
|
||||
size_t max_segment_len = workspace_buf_length(fs_info);
|
||||
int ret;
|
||||
|
||||
if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2))
|
||||
if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) {
|
||||
btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)",
|
||||
srclen, LZO_LEN, max_segment_len + LZO_LEN * 2);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
in_len = get_unaligned_le32(data_in);
|
||||
if (unlikely(in_len != srclen))
|
||||
if (unlikely(in_len != srclen)) {
|
||||
btrfs_err(fs_info, "invalid lzo header length, has %zu expect %zu",
|
||||
in_len, srclen);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
data_in += LZO_LEN;
|
||||
|
||||
in_len = get_unaligned_le32(data_in);
|
||||
if (unlikely(in_len != srclen - LZO_LEN * 2))
|
||||
if (unlikely(in_len != srclen - LZO_LEN * 2)) {
|
||||
btrfs_err(fs_info, "invalid lzo segment length, has %zu expect %zu",
|
||||
in_len, srclen - LZO_LEN * 2);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
data_in += LZO_LEN;
|
||||
|
||||
out_len = sectorsize;
|
||||
|
||||
@@ -449,9 +449,9 @@ void btrfs_print_leaf(const struct extent_buffer *l)
|
||||
nr = btrfs_header_nritems(l);
|
||||
|
||||
btrfs_info(fs_info,
|
||||
"leaf %llu gen %llu total ptrs %d free space %d owner %llu",
|
||||
"leaf %llu gen %llu total ptrs %d free space %d owner %lld",
|
||||
btrfs_header_bytenr(l), btrfs_header_generation(l), nr,
|
||||
btrfs_leaf_free_space(l), btrfs_header_owner(l));
|
||||
btrfs_leaf_free_space(l), (s64)btrfs_header_owner(l));
|
||||
print_eb_refs_lock(l);
|
||||
for (i = 0 ; i < nr ; i++) {
|
||||
char key_buf[KEY_TYPE_BUF_SIZE];
|
||||
@@ -600,10 +600,10 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow)
|
||||
return;
|
||||
}
|
||||
btrfs_info(fs_info,
|
||||
"node %llu level %d gen %llu total ptrs %d free spc %u owner %llu",
|
||||
"node %llu level %d gen %llu total ptrs %d free spc %u owner %lld",
|
||||
btrfs_header_bytenr(c), level, btrfs_header_generation(c),
|
||||
nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr,
|
||||
btrfs_header_owner(c));
|
||||
(s64)btrfs_header_owner(c));
|
||||
print_eb_refs_lock(c);
|
||||
for (i = 0; i < nr; i++) {
|
||||
btrfs_node_key_to_cpu(c, &key, i);
|
||||
|
||||
@@ -127,14 +127,24 @@ int btrfs_set_prop(struct btrfs_trans_handle *trans, struct btrfs_inode *inode,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = handler->validate(inode, value, value_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, value,
|
||||
value_len, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = handler->apply(inode, value, value_len);
|
||||
if (ret) {
|
||||
btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, NULL,
|
||||
0, flags);
|
||||
/* We validated before, so it should not fail here. */
|
||||
ASSERT(ret == 0);
|
||||
if (unlikely(ret)) {
|
||||
int ret2;
|
||||
|
||||
/* Try to delete xattr, if not possible abort transaction. */
|
||||
ret2 = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name,
|
||||
NULL, 0, flags);
|
||||
if (unlikely(ret2))
|
||||
btrfs_abort_transaction(trans, ret2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -719,21 +719,19 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
|
||||
|
||||
ret = btrfs_insert_root(trans, fs_info->tree_root,
|
||||
&root_key, root_item);
|
||||
if (ret)
|
||||
goto abort;
|
||||
if (unlikely(ret)) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
reloc_root = btrfs_read_tree_root(fs_info->tree_root, &root_key);
|
||||
if (IS_ERR(reloc_root)) {
|
||||
ret = PTR_ERR(reloc_root);
|
||||
goto abort;
|
||||
btrfs_abort_transaction(trans, PTR_ERR(reloc_root));
|
||||
return ERR_CAST(reloc_root);
|
||||
}
|
||||
set_bit(BTRFS_ROOT_SHAREABLE, &reloc_root->state);
|
||||
btrfs_set_root_last_trans(reloc_root, trans->transid);
|
||||
return reloc_root;
|
||||
|
||||
abort:
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1912,6 +1910,7 @@ void merge_reloc_roots(struct reloc_control *rc)
|
||||
* corruption, e.g. bad reloc tree key offset.
|
||||
*/
|
||||
ret = -EINVAL;
|
||||
btrfs_put_root(root);
|
||||
goto out;
|
||||
}
|
||||
ret = merge_reloc_root(rc, root);
|
||||
|
||||
@@ -1923,6 +1923,12 @@ static int check_inode_ref(struct extent_buffer *leaf,
|
||||
|
||||
iref = (struct btrfs_inode_ref *)ptr;
|
||||
namelen = btrfs_inode_ref_name_len(leaf, iref);
|
||||
if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) {
|
||||
inode_ref_err(leaf, slot,
|
||||
"invalid inode ref name length, has %u expect [1, %u]",
|
||||
namelen, BTRFS_NAME_LEN);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
if (unlikely(ptr + sizeof(*iref) + namelen > end)) {
|
||||
inode_ref_err(leaf, slot,
|
||||
"inode ref overflow, ptr %lu end %lu namelen %u",
|
||||
|
||||
Reference in New Issue
Block a user