summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/binfmt_elf_fdpic.c6
-rw-r--r--fs/btrfs/backref.c28
-rw-r--r--fs/btrfs/block-group.c36
-rw-r--r--fs/btrfs/compression.c11
-rw-r--r--fs/btrfs/disk-io.c20
-rw-r--r--fs/btrfs/extent-tree.c98
-rw-r--r--fs/btrfs/file-item.c7
-rw-r--r--fs/btrfs/free-space-tree.c9
-rw-r--r--fs/btrfs/inode.c25
-rw-r--r--fs/btrfs/ioctl.c12
-rw-r--r--fs/btrfs/lzo.c4
-rw-r--r--fs/btrfs/qgroup.c8
-rw-r--r--fs/btrfs/raid56.c12
-rw-r--r--fs/btrfs/relocation.c39
-rw-r--r--fs/btrfs/tree-checker.c19
-rw-r--r--fs/btrfs/tree-log.c27
-rw-r--r--fs/btrfs/volumes.c27
-rw-r--r--fs/btrfs/zoned.c13
-rw-r--r--fs/btrfs/zstd.c2
-rw-r--r--fs/nfsd/export.c63
-rw-r--r--fs/nfsd/export.h7
-rw-r--r--fs/nfsd/nfs4xdr.c9
-rw-r--r--fs/nfsd/nfsctl.c22
-rw-r--r--fs/nfsd/state.h17
-rw-r--r--fs/smb/client/cifsglob.h6
-rw-r--r--fs/smb/client/connect.c4
-rw-r--r--fs/smb/client/file.c1
-rw-r--r--fs/smb/client/inode.c21
-rw-r--r--fs/smb/client/smb1transport.c2
-rw-r--r--fs/smb/client/smb2ops.c20
-rw-r--r--fs/smb/server/mgmt/tree_connect.c9
-rw-r--r--fs/smb/server/smb2pdu.c17
-rw-r--r--fs/tests/exec_kunit.c3
33 files changed, 495 insertions, 109 deletions
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 95b1d0852135..95b65aab7daa 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -595,6 +595,12 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
#ifdef ELF_HWCAP2
nitems++;
#endif
+#ifdef ELF_HWCAP3
+ nitems++;
+#endif
+#ifdef ELF_HWCAP4
+ nitems++;
+#endif
csp = sp;
sp -= nitems * 2 * sizeof(unsigned long);
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index af98421e7922..0428557fd77b 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -1393,6 +1393,13 @@ static int find_parent_nodes(struct btrfs_backref_walk_ctx *ctx,
.indirect_missing_keys = PREFTREE_INIT
};
+ if (unlikely(!root)) {
+ btrfs_err(ctx->fs_info,
+ "missing extent root for extent at bytenr %llu",
+ ctx->bytenr);
+ return -EUCLEAN;
+ }
+
/* Roots ulist is not needed when using a sharedness check context. */
if (sc)
ASSERT(ctx->roots == NULL);
@@ -2204,6 +2211,13 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
struct btrfs_extent_item *ei;
struct btrfs_key key;
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ logical);
+ return -EUCLEAN;
+ }
+
key.objectid = logical;
if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
key.type = BTRFS_METADATA_ITEM_KEY;
@@ -2851,6 +2865,13 @@ int btrfs_backref_iter_start(struct btrfs_backref_iter *iter, u64 bytenr)
struct btrfs_key key;
int ret;
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
key.type = BTRFS_METADATA_ITEM_KEY;
key.offset = (u64)-1;
@@ -2987,6 +3008,13 @@ int btrfs_backref_iter_next(struct btrfs_backref_iter *iter)
/* We're at keyed items, there is no inline item, go to the next one */
extent_root = btrfs_extent_root(iter->fs_info, iter->bytenr);
+ if (unlikely(!extent_root)) {
+ btrfs_err(iter->fs_info,
+ "missing extent root for extent at bytenr %llu",
+ iter->bytenr);
+ return -EUCLEAN;
+ }
+
ret = btrfs_next_item(extent_root, iter->path);
if (ret)
return ret;
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index 2a886bece810..ebf5079096af 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -739,6 +739,12 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl)
last = max_t(u64, block_group->start, BTRFS_SUPER_INFO_OFFSET);
extent_root = btrfs_extent_root(fs_info, last);
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for block group at offset %llu",
+ block_group->start);
+ return -EUCLEAN;
+ }
#ifdef CONFIG_BTRFS_DEBUG
/*
@@ -1061,6 +1067,11 @@ static int remove_block_group_item(struct btrfs_trans_handle *trans,
int ret;
root = btrfs_block_group_root(fs_info);
+ if (unlikely(!root)) {
+ btrfs_err(fs_info, "missing block group root");
+ return -EUCLEAN;
+ }
+
key.objectid = block_group->start;
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
key.offset = block_group->length;
@@ -1349,6 +1360,11 @@ struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
struct btrfs_chunk_map *map;
unsigned int num_items;
+ if (unlikely(!root)) {
+ btrfs_err(fs_info, "missing block group root");
+ return ERR_PTR(-EUCLEAN);
+ }
+
map = btrfs_find_chunk_map(fs_info, chunk_offset, 1);
ASSERT(map != NULL);
ASSERT(map->start == chunk_offset);
@@ -2140,6 +2156,11 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info,
int ret;
struct btrfs_key found_key;
+ if (unlikely(!root)) {
+ btrfs_err(fs_info, "missing block group root");
+ return -EUCLEAN;
+ }
+
btrfs_for_each_slot(root, key, &found_key, path, ret) {
if (found_key.objectid >= key->objectid &&
found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
@@ -2713,6 +2734,11 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans,
size_t size;
int ret;
+ if (unlikely(!root)) {
+ btrfs_err(fs_info, "missing block group root");
+ return -EUCLEAN;
+ }
+
spin_lock(&block_group->lock);
btrfs_set_stack_block_group_v2_used(&bgi, block_group->used);
btrfs_set_stack_block_group_v2_chunk_objectid(&bgi, block_group->global_root_id);
@@ -3048,6 +3074,11 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
int ret;
bool dirty_bg_running;
+ if (unlikely(!root)) {
+ btrfs_err(fs_info, "missing block group root");
+ return -EUCLEAN;
+ }
+
/*
* This can only happen when we are doing read-only scrub on read-only
* mount.
@@ -3192,6 +3223,11 @@ static int update_block_group_item(struct btrfs_trans_handle *trans,
u64 used, remap_bytes;
u32 identity_remap_count;
+ if (unlikely(!root)) {
+ btrfs_err(fs_info, "missing block group root");
+ return -EUCLEAN;
+ }
+
/*
* Block group items update can be triggered out of commit transaction
* critical section, thus we need a consistent view of used bytes.
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 790518a8c803..85199944c1eb 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -320,10 +320,16 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
ASSERT(IS_ALIGNED(ordered->file_offset, fs_info->sectorsize));
ASSERT(IS_ALIGNED(ordered->num_bytes, fs_info->sectorsize));
- ASSERT(cb->writeback);
+ /*
+ * This flag determines if we should clear the writeback flag from the
+ * page cache. But this function is only utilized by encoded writes, it
+ * never goes through the page cache.
+ */
+ ASSERT(!cb->writeback);
cb->start = ordered->file_offset;
cb->len = ordered->num_bytes;
+ ASSERT(cb->bbio.bio.bi_iter.bi_size == ordered->disk_num_bytes);
cb->compressed_len = ordered->disk_num_bytes;
cb->bbio.bio.bi_iter.bi_sector = ordered->disk_bytenr >> SECTOR_SHIFT;
cb->bbio.ordered = ordered;
@@ -345,8 +351,7 @@ struct compressed_bio *btrfs_alloc_compressed_write(struct btrfs_inode *inode,
cb = alloc_compressed_bio(inode, start, REQ_OP_WRITE, end_bbio_compressed_write);
cb->start = start;
cb->len = len;
- cb->writeback = true;
-
+ cb->writeback = false;
return cb;
}
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 407830d86d0d..01f2dbb69832 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1591,7 +1591,7 @@ static int find_newest_super_backup(struct btrfs_fs_info *info)
* this will bump the backup pointer by one when it is
* done
*/
-static void backup_super_roots(struct btrfs_fs_info *info)
+static int backup_super_roots(struct btrfs_fs_info *info)
{
const int next_backup = info->backup_root_index;
struct btrfs_root_backup *root_backup;
@@ -1623,6 +1623,15 @@ static void backup_super_roots(struct btrfs_fs_info *info)
struct btrfs_root *extent_root = btrfs_extent_root(info, 0);
struct btrfs_root *csum_root = btrfs_csum_root(info, 0);
+ if (unlikely(!extent_root)) {
+ btrfs_err(info, "missing extent root for extent at bytenr 0");
+ return -EUCLEAN;
+ }
+ if (unlikely(!csum_root)) {
+ btrfs_err(info, "missing csum root for extent at bytenr 0");
+ return -EUCLEAN;
+ }
+
btrfs_set_backup_extent_root(root_backup,
extent_root->node->start);
btrfs_set_backup_extent_root_gen(root_backup,
@@ -1670,6 +1679,8 @@ static void backup_super_roots(struct btrfs_fs_info *info)
memcpy(&info->super_copy->super_roots,
&info->super_for_commit->super_roots,
sizeof(*root_backup) * BTRFS_NUM_BACKUP_ROOTS);
+
+ return 0;
}
/*
@@ -4051,8 +4062,11 @@ int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors)
* not from fsync where the tree roots in fs_info have not
* been consistent on disk.
*/
- if (max_mirrors == 0)
- backup_super_roots(fs_info);
+ if (max_mirrors == 0) {
+ ret = backup_super_roots(fs_info);
+ if (ret < 0)
+ return ret;
+ }
sb = fs_info->super_for_commit;
dev_item = &sb->dev_item;
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index b0d9baf5b412..85ee5c79759d 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -75,6 +75,12 @@ int btrfs_lookup_data_extent(struct btrfs_fs_info *fs_info, u64 start, u64 len)
struct btrfs_key key;
BTRFS_PATH_AUTO_FREE(path);
+ if (unlikely(!root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu", start);
+ return -EUCLEAN;
+ }
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -131,6 +137,12 @@ search_again:
key.offset = offset;
extent_root = btrfs_extent_root(fs_info, bytenr);
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
if (ret < 0)
return ret;
@@ -436,6 +448,12 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
int recow;
int ret;
+ if (unlikely(!root)) {
+ btrfs_err(trans->fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
if (parent) {
key.type = BTRFS_SHARED_DATA_REF_KEY;
@@ -510,6 +528,12 @@ static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans,
u32 num_refs;
int ret;
+ if (unlikely(!root)) {
+ btrfs_err(trans->fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
if (node->parent) {
key.type = BTRFS_SHARED_DATA_REF_KEY;
@@ -668,6 +692,12 @@ static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans,
struct btrfs_key key;
int ret;
+ if (unlikely(!root)) {
+ btrfs_err(trans->fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
if (parent) {
key.type = BTRFS_SHARED_BLOCK_REF_KEY;
@@ -692,6 +722,12 @@ static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans,
struct btrfs_key key;
int ret;
+ if (unlikely(!root)) {
+ btrfs_err(trans->fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
if (node->parent) {
key.type = BTRFS_SHARED_BLOCK_REF_KEY;
@@ -782,6 +818,12 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
int needed;
+ if (unlikely(!root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = num_bytes;
@@ -1680,6 +1722,12 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
}
root = btrfs_extent_root(fs_info, key.objectid);
+ if (unlikely(!root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ key.objectid);
+ return -EUCLEAN;
+ }
again:
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
if (ret < 0) {
@@ -1926,8 +1974,15 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans,
struct btrfs_root *csum_root;
csum_root = btrfs_csum_root(fs_info, head->bytenr);
- ret = btrfs_del_csums(trans, csum_root, head->bytenr,
- head->num_bytes);
+ if (unlikely(!csum_root)) {
+ btrfs_err(fs_info,
+ "missing csum root for extent at bytenr %llu",
+ head->bytenr);
+ ret = -EUCLEAN;
+ } else {
+ ret = btrfs_del_csums(trans, csum_root, head->bytenr,
+ head->num_bytes);
+ }
}
}
@@ -2379,6 +2434,12 @@ static noinline int check_committed_ref(struct btrfs_inode *inode,
int type;
int ret;
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
+
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = (u64)-1;
@@ -3093,6 +3154,15 @@ static int do_free_extent_accounting(struct btrfs_trans_handle *trans,
struct btrfs_root *csum_root;
csum_root = btrfs_csum_root(trans->fs_info, bytenr);
+ if (unlikely(!csum_root)) {
+ ret = -EUCLEAN;
+ btrfs_abort_transaction(trans, ret);
+ btrfs_err(trans->fs_info,
+ "missing csum root for extent at bytenr %llu",
+ bytenr);
+ return ret;
+ }
+
ret = btrfs_del_csums(trans, csum_root, bytenr, num_bytes);
if (unlikely(ret)) {
btrfs_abort_transaction(trans, ret);
@@ -3222,7 +3292,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
u64 delayed_ref_root = href->owning_root;
extent_root = btrfs_extent_root(info, bytenr);
- ASSERT(extent_root);
+ if (unlikely(!extent_root)) {
+ btrfs_err(info,
+ "missing extent root for extent at bytenr %llu", bytenr);
+ return -EUCLEAN;
+ }
path = btrfs_alloc_path();
if (!path)
@@ -4939,11 +5013,18 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
size += btrfs_extent_inline_ref_size(BTRFS_EXTENT_OWNER_REF_KEY);
size += btrfs_extent_inline_ref_size(type);
+ extent_root = btrfs_extent_root(fs_info, ins->objectid);
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ ins->objectid);
+ return -EUCLEAN;
+ }
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
- extent_root = btrfs_extent_root(fs_info, ins->objectid);
ret = btrfs_insert_empty_item(trans, extent_root, path, ins, size);
if (ret) {
btrfs_free_path(path);
@@ -5019,11 +5100,18 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
size += sizeof(*block_info);
}
+ extent_root = btrfs_extent_root(fs_info, extent_key.objectid);
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ extent_key.objectid);
+ return -EUCLEAN;
+ }
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
- extent_root = btrfs_extent_root(fs_info, extent_key.objectid);
ret = btrfs_insert_empty_item(trans, extent_root, path, &extent_key,
size);
if (ret) {
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 927324a2175a..ed8ecf44fbd0 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -308,6 +308,13 @@ static int search_csum_tree(struct btrfs_fs_info *fs_info,
/* Current item doesn't contain the desired range, search again */
btrfs_release_path(path);
csum_root = btrfs_csum_root(fs_info, disk_bytenr);
+ if (unlikely(!csum_root)) {
+ btrfs_err(fs_info,
+ "missing csum root for extent at bytenr %llu",
+ disk_bytenr);
+ return -EUCLEAN;
+ }
+
item = btrfs_lookup_csum(NULL, csum_root, path, disk_bytenr, 0);
if (IS_ERR(item)) {
ret = PTR_ERR(item);
diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c
index ecddfca92b2b..9efd1ec90f03 100644
--- a/fs/btrfs/free-space-tree.c
+++ b/fs/btrfs/free-space-tree.c
@@ -1073,6 +1073,14 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans,
if (ret)
return ret;
+ extent_root = btrfs_extent_root(trans->fs_info, block_group->start);
+ if (unlikely(!extent_root)) {
+ btrfs_err(trans->fs_info,
+ "missing extent root for block group at offset %llu",
+ block_group->start);
+ return -EUCLEAN;
+ }
+
mutex_lock(&block_group->free_space_lock);
/*
@@ -1086,7 +1094,6 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans,
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
- extent_root = btrfs_extent_root(trans->fs_info, key.objectid);
ret = btrfs_search_slot_for_read(extent_root, &key, path, 1, 0);
if (ret < 0)
goto out_locked;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a6da98435ef7..f643a0520872 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2012,6 +2012,13 @@ static int can_nocow_file_extent(struct btrfs_path *path,
*/
csum_root = btrfs_csum_root(root->fs_info, io_start);
+ if (unlikely(!csum_root)) {
+ btrfs_err(root->fs_info,
+ "missing csum root for extent at bytenr %llu", io_start);
+ ret = -EUCLEAN;
+ goto out;
+ }
+
ret = btrfs_lookup_csums_list(csum_root, io_start,
io_start + args->file_extent.num_bytes - 1,
NULL, nowait);
@@ -2749,10 +2756,17 @@ static int add_pending_csums(struct btrfs_trans_handle *trans,
int ret;
list_for_each_entry(sum, list, list) {
- trans->adding_csums = true;
- if (!csum_root)
+ if (!csum_root) {
csum_root = btrfs_csum_root(trans->fs_info,
sum->logical);
+ if (unlikely(!csum_root)) {
+ btrfs_err(trans->fs_info,
+ "missing csum root for extent at bytenr %llu",
+ sum->logical);
+ return -EUCLEAN;
+ }
+ }
+ trans->adding_csums = true;
ret = btrfs_csum_file_blocks(trans, csum_root, sum);
trans->adding_csums = false;
if (ret)
@@ -9874,6 +9888,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
int compression;
size_t orig_count;
const u32 min_folio_size = btrfs_min_folio_size(fs_info);
+ const u32 blocksize = fs_info->sectorsize;
u64 start, end;
u64 num_bytes, ram_bytes, disk_num_bytes;
struct btrfs_key ins;
@@ -9984,9 +9999,9 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
ret = -EFAULT;
goto out_cb;
}
- if (bytes < min_folio_size)
- folio_zero_range(folio, bytes, min_folio_size - bytes);
- ret = bio_add_folio(&cb->bbio.bio, folio, folio_size(folio), 0);
+ if (!IS_ALIGNED(bytes, blocksize))
+ folio_zero_range(folio, bytes, round_up(bytes, blocksize) - bytes);
+ ret = bio_add_folio(&cb->bbio.bio, folio, round_up(bytes, blocksize), 0);
if (unlikely(!ret)) {
folio_put(folio);
ret = -EINVAL;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index b805dd9227ef..d75d31b606e4 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3617,7 +3617,8 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
}
}
- trans = btrfs_join_transaction(root);
+ /* 2 BTRFS_QGROUP_RELATION_KEY items. */
+ trans = btrfs_start_transaction(root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
@@ -3689,7 +3690,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
goto out;
}
- trans = btrfs_join_transaction(root);
+ /*
+ * 1 BTRFS_QGROUP_INFO_KEY item.
+ * 1 BTRFS_QGROUP_LIMIT_KEY item.
+ */
+ trans = btrfs_start_transaction(root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
@@ -3738,7 +3743,8 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
goto drop_write;
}
- trans = btrfs_join_transaction(root);
+ /* 1 BTRFS_QGROUP_LIMIT_KEY item. */
+ trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 049a940ba449..79642e02181b 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -429,7 +429,7 @@ static void copy_compressed_segment(struct compressed_bio *cb,
int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
{
struct workspace *workspace = list_entry(ws, struct workspace, list);
- const struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info;
+ struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info;
const u32 sectorsize = fs_info->sectorsize;
struct folio_iter fi;
char *kaddr;
@@ -447,7 +447,7 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
/* There must be a compressed folio and matches the sectorsize. */
if (unlikely(!fi.folio))
return -EINVAL;
- ASSERT(folio_size(fi.folio) == sectorsize);
+ ASSERT(folio_size(fi.folio) == btrfs_min_folio_size(fs_info));
kaddr = kmap_local_folio(fi.folio, 0);
len_in = read_compress_length(kaddr);
kunmap_local(kaddr);
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 3b2a6517d0b5..41589ce66371 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3739,6 +3739,14 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
mutex_lock(&fs_info->qgroup_rescan_lock);
extent_root = btrfs_extent_root(fs_info,
fs_info->qgroup_rescan_progress.objectid);
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ fs_info->qgroup_rescan_progress.objectid);
+ mutex_unlock(&fs_info->qgroup_rescan_lock);
+ return -EUCLEAN;
+ }
+
ret = btrfs_search_slot_for_read(extent_root,
&fs_info->qgroup_rescan_progress,
path, 1, 0);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index b4511f560e92..02105d68accb 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -2297,8 +2297,7 @@ void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
static void fill_data_csums(struct btrfs_raid_bio *rbio)
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
- struct btrfs_root *csum_root = btrfs_csum_root(fs_info,
- rbio->bioc->full_stripe_logical);
+ struct btrfs_root *csum_root;
const u64 start = rbio->bioc->full_stripe_logical;
const u32 len = (rbio->nr_data * rbio->stripe_nsectors) <<
fs_info->sectorsize_bits;
@@ -2331,6 +2330,15 @@ static void fill_data_csums(struct btrfs_raid_bio *rbio)
goto error;
}
+ csum_root = btrfs_csum_root(fs_info, rbio->bioc->full_stripe_logical);
+ if (unlikely(!csum_root)) {
+ btrfs_err(fs_info,
+ "missing csum root for extent at bytenr %llu",
+ rbio->bioc->full_stripe_logical);
+ ret = -EUCLEAN;
+ goto error;
+ }
+
ret = btrfs_lookup_csums_bitmap(csum_root, NULL, start, start + len - 1,
rbio->csum_buf, rbio->csum_bitmap);
if (ret < 0)
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index b2343aed7a5d..033f74fd6225 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -4185,6 +4185,8 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
dest_addr = ins.objectid;
dest_length = ins.offset;
+ dest_bg = btrfs_lookup_block_group(fs_info, dest_addr);
+
if (!is_data && !IS_ALIGNED(dest_length, fs_info->nodesize)) {
u64 new_length = ALIGN_DOWN(dest_length, fs_info->nodesize);
@@ -4295,15 +4297,12 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
if (unlikely(ret))
goto end;
- dest_bg = btrfs_lookup_block_group(fs_info, dest_addr);
-
adjust_block_group_remap_bytes(trans, dest_bg, dest_length);
mutex_lock(&dest_bg->free_space_lock);
bg_needs_free_space = test_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE,
&dest_bg->runtime_flags);
mutex_unlock(&dest_bg->free_space_lock);
- btrfs_put_block_group(dest_bg);
if (bg_needs_free_space) {
ret = btrfs_add_block_group_free_space(trans, dest_bg);
@@ -4333,13 +4332,13 @@ end:
btrfs_end_transaction(trans);
}
} else {
- dest_bg = btrfs_lookup_block_group(fs_info, dest_addr);
btrfs_free_reserved_bytes(dest_bg, dest_length, 0);
- btrfs_put_block_group(dest_bg);
ret = btrfs_commit_transaction(trans);
}
+ btrfs_put_block_group(dest_bg);
+
return ret;
}
@@ -4954,6 +4953,12 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info,
struct btrfs_space_info *sinfo = src_bg->space_info;
extent_root = btrfs_extent_root(fs_info, src_bg->start);
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for block group at offset %llu",
+ src_bg->start);
+ return -EUCLEAN;
+ }
trans = btrfs_start_transaction(extent_root, 0);
if (IS_ERR(trans))
@@ -5306,6 +5311,13 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
int ret;
bool bg_is_ro = false;
+ if (unlikely(!extent_root)) {
+ btrfs_err(fs_info,
+ "missing extent root for block group at offset %llu",
+ group_start);
+ return -EUCLEAN;
+ }
+
/*
* This only gets set if we had a half-deleted snapshot on mount. We
* cannot allow relocation to start while we're still trying to clean up
@@ -5536,12 +5548,17 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info)
goto out;
}
+ rc->extent_root = btrfs_extent_root(fs_info, 0);
+ if (unlikely(!rc->extent_root)) {
+ btrfs_err(fs_info, "missing extent root for extent at bytenr 0");
+ ret = -EUCLEAN;
+ goto out;
+ }
+
ret = reloc_chunk_start(fs_info);
if (ret < 0)
goto out_end;
- rc->extent_root = btrfs_extent_root(fs_info, 0);
-
set_reloc_control(rc);
trans = btrfs_join_transaction(rc->extent_root);
@@ -5636,6 +5653,14 @@ int btrfs_reloc_clone_csums(struct btrfs_ordered_extent *ordered)
LIST_HEAD(list);
int ret;
+ if (unlikely(!csum_root)) {
+ btrfs_mark_ordered_extent_error(ordered);
+ btrfs_err(fs_info,
+ "missing csum root for extent at bytenr %llu",
+ disk_bytenr);
+ return -EUCLEAN;
+ }
+
ret = btrfs_lookup_csums_list(csum_root, disk_bytenr,
disk_bytenr + ordered->num_bytes - 1,
&list, false);
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index ac4c4573ee39..b4e114efff45 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1284,10 +1284,27 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key,
}
if (unlikely(btrfs_root_drop_level(&ri) >= BTRFS_MAX_LEVEL)) {
generic_err(leaf, slot,
- "invalid root level, have %u expect [0, %u]",
+ "invalid root drop_level, have %u expect [0, %u]",
btrfs_root_drop_level(&ri), BTRFS_MAX_LEVEL - 1);
return -EUCLEAN;
}
+ /*
+ * If drop_progress.objectid is non-zero, a btrfs_drop_snapshot() was
+ * interrupted and the resume point was recorded in drop_progress and
+ * drop_level. In that case drop_level must be >= 1: level 0 is the
+ * leaf level and drop_snapshot never saves a checkpoint there (it
+ * only records checkpoints at internal node levels in DROP_REFERENCE
+ * stage). A zero drop_level combined with a non-zero drop_progress
+ * objectid indicates on-disk corruption and would cause a BUG_ON in
+ * merge_reloc_root() and btrfs_drop_snapshot() at mount time.
+ */
+ if (unlikely(btrfs_disk_key_objectid(&ri.drop_progress) != 0 &&
+ btrfs_root_drop_level(&ri) == 0)) {
+ generic_err(leaf, slot,
+ "invalid root drop_level 0 with non-zero drop_progress objectid %llu",
+ btrfs_disk_key_objectid(&ri.drop_progress));
+ return -EUCLEAN;
+ }
/* Flags check */
if (unlikely(btrfs_root_flags(&ri) & ~valid_root_flags)) {
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 780a06d59240..ab0d460c7139 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -984,6 +984,13 @@ static noinline int replay_one_extent(struct walk_control *wc)
sums = list_first_entry(&ordered_sums, struct btrfs_ordered_sum, list);
csum_root = btrfs_csum_root(fs_info, sums->logical);
+ if (unlikely(!csum_root)) {
+ btrfs_err(fs_info,
+ "missing csum root for extent at bytenr %llu",
+ sums->logical);
+ ret = -EUCLEAN;
+ }
+
if (!ret) {
ret = btrfs_del_csums(trans, csum_root, sums->logical,
sums->len);
@@ -4890,6 +4897,13 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
}
csum_root = btrfs_csum_root(trans->fs_info, disk_bytenr);
+ if (unlikely(!csum_root)) {
+ btrfs_err(trans->fs_info,
+ "missing csum root for extent at bytenr %llu",
+ disk_bytenr);
+ return -EUCLEAN;
+ }
+
disk_bytenr += extent_offset;
ret = btrfs_lookup_csums_list(csum_root, disk_bytenr,
disk_bytenr + extent_num_bytes - 1,
@@ -5086,6 +5100,13 @@ static int log_extent_csums(struct btrfs_trans_handle *trans,
/* block start is already adjusted for the file extent offset. */
block_start = btrfs_extent_map_block_start(em);
csum_root = btrfs_csum_root(trans->fs_info, block_start);
+ if (unlikely(!csum_root)) {
+ btrfs_err(trans->fs_info,
+ "missing csum root for extent at bytenr %llu",
+ block_start);
+ return -EUCLEAN;
+ }
+
ret = btrfs_lookup_csums_list(csum_root, block_start + csum_offset,
block_start + csum_offset + csum_len - 1,
&ordered_sums, false);
@@ -6195,6 +6216,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_log_ctx *ctx)
{
+ const bool orig_log_new_dentries = ctx->log_new_dentries;
int ret = 0;
/*
@@ -6256,7 +6278,11 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
* dir index key range logged for the directory. So we
* must make sure the deletion is recorded.
*/
+ ctx->log_new_dentries = false;
ret = btrfs_log_inode(trans, inode, LOG_INODE_ALL, ctx);
+ if (!ret && ctx->log_new_dentries)
+ ret = log_new_dir_dentries(trans, inode, ctx);
+
btrfs_add_delayed_iput(inode);
if (ret)
break;
@@ -6291,6 +6317,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
break;
}
+ ctx->log_new_dentries = orig_log_new_dentries;
ctx->logging_conflict_inodes = false;
if (ret)
free_conflicting_inodes(ctx);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 648bb09fc416..ab51e6ecfdef 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -3587,7 +3587,7 @@ int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset, bool v
/* step one, relocate all the extents inside this chunk */
btrfs_scrub_pause(fs_info);
- ret = btrfs_relocate_block_group(fs_info, chunk_offset, true);
+ ret = btrfs_relocate_block_group(fs_info, chunk_offset, verbose);
btrfs_scrub_continue(fs_info);
if (ret) {
/*
@@ -4277,20 +4277,29 @@ static int balance_remap_chunks(struct btrfs_fs_info *fs_info, struct btrfs_path
end:
while (!list_empty(chunks)) {
bool is_unused;
+ struct btrfs_block_group *bg;
rci = list_first_entry(chunks, struct remap_chunk_info, list);
- spin_lock(&rci->bg->lock);
- is_unused = !btrfs_is_block_group_used(rci->bg);
- spin_unlock(&rci->bg->lock);
+ bg = rci->bg;
+ if (bg) {
+ /*
+ * This is a bit racy and the 'used' status can change
+ * but this is not a problem as later functions will
+ * verify it again.
+ */
+ spin_lock(&bg->lock);
+ is_unused = !btrfs_is_block_group_used(bg);
+ spin_unlock(&bg->lock);
- if (is_unused)
- btrfs_mark_bg_unused(rci->bg);
+ if (is_unused)
+ btrfs_mark_bg_unused(bg);
- if (rci->made_ro)
- btrfs_dec_block_group_ro(rci->bg);
+ if (rci->made_ro)
+ btrfs_dec_block_group_ro(bg);
- btrfs_put_block_group(rci->bg);
+ btrfs_put_block_group(bg);
+ }
list_del(&rci->list);
kfree(rci);
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 39930d99943c..0cd7fd3fcfa3 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -337,7 +337,10 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
if (!btrfs_fs_incompat(fs_info, ZONED))
return 0;
- mutex_lock(&fs_devices->device_list_mutex);
+ /*
+ * No need to take the device_list mutex here, we're still in the mount
+ * path and devices cannot be added to or removed from the list yet.
+ */
list_for_each_entry(device, &fs_devices->devices, dev_list) {
/* We can skip reading of zone info for missing devices */
if (!device->bdev)
@@ -347,7 +350,6 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
if (ret)
break;
}
- mutex_unlock(&fs_devices->device_list_mutex);
return ret;
}
@@ -1259,6 +1261,13 @@ static int calculate_alloc_pointer(struct btrfs_block_group *cache,
key.offset = 0;
root = btrfs_extent_root(fs_info, key.objectid);
+ if (unlikely(!root)) {
+ btrfs_err(fs_info,
+ "missing extent root for extent at bytenr %llu",
+ key.objectid);
+ return -EUCLEAN;
+ }
+
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
/* We should not find the exact match */
if (unlikely(!ret))
diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c
index 6f076fa8f3f9..3e847b91dae3 100644
--- a/fs/btrfs/zstd.c
+++ b/fs/btrfs/zstd.c
@@ -600,7 +600,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
bio_first_folio(&fi, &cb->bbio.bio, 0);
if (unlikely(!fi.folio))
return -EINVAL;
- ASSERT(folio_size(fi.folio) == blocksize);
+ ASSERT(folio_size(fi.folio) == min_folio_size);
stream = zstd_init_dstream(
ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 8fdbba7cad96..8e8a76a44ff0 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -36,19 +36,30 @@
* second map contains a reference to the entry in the first map.
*/
+static struct workqueue_struct *nfsd_export_wq;
+
#define EXPKEY_HASHBITS 8
#define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS)
#define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1)
-static void expkey_put(struct kref *ref)
+static void expkey_release(struct work_struct *work)
{
- struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
+ struct svc_expkey *key = container_of(to_rcu_work(work),
+ struct svc_expkey, ek_rwork);
if (test_bit(CACHE_VALID, &key->h.flags) &&
!test_bit(CACHE_NEGATIVE, &key->h.flags))
path_put(&key->ek_path);
auth_domain_put(key->ek_client);
- kfree_rcu(key, ek_rcu);
+ kfree(key);
+}
+
+static void expkey_put(struct kref *ref)
+{
+ struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
+
+ INIT_RCU_WORK(&key->ek_rwork, expkey_release);
+ queue_rcu_work(nfsd_export_wq, &key->ek_rwork);
}
static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
@@ -353,11 +364,13 @@ static void export_stats_destroy(struct export_stats *stats)
EXP_STATS_COUNTERS_NUM);
}
-static void svc_export_release(struct rcu_head *rcu_head)
+static void svc_export_release(struct work_struct *work)
{
- struct svc_export *exp = container_of(rcu_head, struct svc_export,
- ex_rcu);
+ struct svc_export *exp = container_of(to_rcu_work(work),
+ struct svc_export, ex_rwork);
+ path_put(&exp->ex_path);
+ auth_domain_put(exp->ex_client);
nfsd4_fslocs_free(&exp->ex_fslocs);
export_stats_destroy(exp->ex_stats);
kfree(exp->ex_stats);
@@ -369,9 +382,8 @@ static void svc_export_put(struct kref *ref)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
- path_put(&exp->ex_path);
- auth_domain_put(exp->ex_client);
- call_rcu(&exp->ex_rcu, svc_export_release);
+ INIT_RCU_WORK(&exp->ex_rwork, svc_export_release);
+ queue_rcu_work(nfsd_export_wq, &exp->ex_rwork);
}
static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
@@ -1479,6 +1491,36 @@ const struct seq_operations nfs_exports_op = {
.show = e_show,
};
+/**
+ * nfsd_export_wq_init - allocate the export release workqueue
+ *
+ * Called once at module load. The workqueue runs deferred svc_export and
+ * svc_expkey release work scheduled by queue_rcu_work() in the cache put
+ * callbacks.
+ *
+ * Return values:
+ * %0: workqueue allocated
+ * %-ENOMEM: allocation failed
+ */
+int nfsd_export_wq_init(void)
+{
+ nfsd_export_wq = alloc_workqueue("nfsd_export", WQ_UNBOUND, 0);
+ if (!nfsd_export_wq)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * nfsd_export_wq_shutdown - drain and free the export release workqueue
+ *
+ * Called once at module unload. Per-namespace teardown in
+ * nfsd_export_shutdown() has already drained all deferred work.
+ */
+void nfsd_export_wq_shutdown(void)
+{
+ destroy_workqueue(nfsd_export_wq);
+}
+
/*
* Initialize the exports module.
*/
@@ -1540,6 +1582,9 @@ nfsd_export_shutdown(struct net *net)
cache_unregister_net(nn->svc_expkey_cache, net);
cache_unregister_net(nn->svc_export_cache, net);
+ /* Drain deferred export and expkey release work. */
+ rcu_barrier();
+ flush_workqueue(nfsd_export_wq);
cache_destroy_net(nn->svc_expkey_cache, net);
cache_destroy_net(nn->svc_export_cache, net);
svcauth_unix_purge(net);
diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h
index d2b09cd76145..b05399374574 100644
--- a/fs/nfsd/export.h
+++ b/fs/nfsd/export.h
@@ -7,6 +7,7 @@
#include <linux/sunrpc/cache.h>
#include <linux/percpu_counter.h>
+#include <linux/workqueue.h>
#include <uapi/linux/nfsd/export.h>
#include <linux/nfs4.h>
@@ -75,7 +76,7 @@ struct svc_export {
u32 ex_layout_types;
struct nfsd4_deviceid_map *ex_devid_map;
struct cache_detail *cd;
- struct rcu_head ex_rcu;
+ struct rcu_work ex_rwork;
unsigned long ex_xprtsec_modes;
struct export_stats *ex_stats;
};
@@ -92,7 +93,7 @@ struct svc_expkey {
u32 ek_fsid[6];
struct path ek_path;
- struct rcu_head ek_rcu;
+ struct rcu_work ek_rwork;
};
#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC))
@@ -110,6 +111,8 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
/*
* Function declarations
*/
+int nfsd_export_wq_init(void);
+void nfsd_export_wq_shutdown(void);
int nfsd_export_init(struct net *);
void nfsd_export_shutdown(struct net *);
void nfsd_export_flush(struct net *);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 41dfba5ab8b8..9d234913100b 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -6281,9 +6281,14 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
int len = xdr->buf->len - (op_status_offset + XDR_UNIT);
so->so_replay.rp_status = op->status;
- so->so_replay.rp_buflen = len;
- read_bytes_from_xdr_buf(xdr->buf, op_status_offset + XDR_UNIT,
+ if (len <= NFSD4_REPLAY_ISIZE) {
+ so->so_replay.rp_buflen = len;
+ read_bytes_from_xdr_buf(xdr->buf,
+ op_status_offset + XDR_UNIT,
so->so_replay.rp_buf, len);
+ } else {
+ so->so_replay.rp_buflen = 0;
+ }
}
status:
op->status = nfsd4_map_status(op->status,
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 4cc8a58fa56a..71aabdaa1d15 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -149,9 +149,19 @@ static int exports_net_open(struct net *net, struct file *file)
seq = file->private_data;
seq->private = nn->svc_export_cache;
+ get_net(net);
return 0;
}
+static int exports_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq = file->private_data;
+ struct cache_detail *cd = seq->private;
+
+ put_net(cd->net);
+ return seq_release(inode, file);
+}
+
static int exports_nfsd_open(struct inode *inode, struct file *file)
{
return exports_net_open(inode->i_sb->s_fs_info, file);
@@ -161,7 +171,7 @@ static const struct file_operations exports_nfsd_operations = {
.open = exports_nfsd_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = exports_release,
};
static int export_features_show(struct seq_file *m, void *v)
@@ -1376,7 +1386,7 @@ static const struct proc_ops exports_proc_ops = {
.proc_open = exports_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
- .proc_release = seq_release,
+ .proc_release = exports_release,
};
static int create_proc_exports_entry(void)
@@ -2259,9 +2269,12 @@ static int __init init_nfsd(void)
if (retval)
goto out_free_pnfs;
nfsd_lockd_init(); /* lockd->nfsd callbacks */
+ retval = nfsd_export_wq_init();
+ if (retval)
+ goto out_free_lockd;
retval = register_pernet_subsys(&nfsd_net_ops);
if (retval < 0)
- goto out_free_lockd;
+ goto out_free_export_wq;
retval = register_cld_notifier();
if (retval)
goto out_free_subsys;
@@ -2290,6 +2303,8 @@ out_free_cld:
unregister_cld_notifier();
out_free_subsys:
unregister_pernet_subsys(&nfsd_net_ops);
+out_free_export_wq:
+ nfsd_export_wq_shutdown();
out_free_lockd:
nfsd_lockd_shutdown();
nfsd_drc_slab_free();
@@ -2310,6 +2325,7 @@ static void __exit exit_nfsd(void)
nfsd4_destroy_laundry_wq();
unregister_cld_notifier();
unregister_pernet_subsys(&nfsd_net_ops);
+ nfsd_export_wq_shutdown();
nfsd_drc_slab_free();
nfsd_lockd_shutdown();
nfsd4_free_slabs();
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 6fcbf1e427d4..c0ca115c3b74 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -541,11 +541,18 @@ struct nfs4_client_reclaim {
struct xdr_netobj cr_princhash;
};
-/* A reasonable value for REPLAY_ISIZE was estimated as follows:
- * The OPEN response, typically the largest, requires
- * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) +
- * 4(deleg. type) + 8(deleg. stateid) + 4(deleg. recall flag) +
- * 20(deleg. space limit) + ~32(deleg. ace) = 112 bytes
+/*
+ * REPLAY_ISIZE is sized for an OPEN response with delegation:
+ * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) +
+ * 8(verifier) + 4(deleg. type) + 8(deleg. stateid) +
+ * 4(deleg. recall flag) + 20(deleg. space limit) +
+ * ~32(deleg. ace) = 112 bytes
+ *
+ * Some responses can exceed this. A LOCK denial includes the conflicting
+ * lock owner, which can be up to 1024 bytes (NFS4_OPAQUE_LIMIT). Responses
+ * larger than REPLAY_ISIZE are not cached in rp_ibuf; only rp_status is
+ * saved. Enlarging this constant increases the size of every
+ * nfs4_stateowner.
*/
#define NFSD4_REPLAY_ISIZE 112
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 7877d327dbb0..709e96e07791 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2386,4 +2386,10 @@ static inline int cifs_open_create_options(unsigned int oflags, int opts)
return opts;
}
+/*
+ * The number of blocks is not related to (i_size / i_blksize), but instead
+ * 512 byte (2**9) size is required for calculating num blocks.
+ */
+#define CIFS_INO_BLOCKS(size) DIV_ROUND_UP_ULL((u64)(size), 512)
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 3bad2c5c523d..69b38f0ccf2b 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1955,6 +1955,10 @@ static int match_session(struct cifs_ses *ses,
case Kerberos:
if (!uid_eq(ctx->cred_uid, ses->cred_uid))
return 0;
+ if (strncmp(ses->user_name ?: "",
+ ctx->username ?: "",
+ CIFS_MAX_USERNAME_LEN))
+ return 0;
break;
case NTLMv2:
case RawNTLMSSP:
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index 27f61fe7e4e2..a69e05f86d7e 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -993,7 +993,6 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
cifs_setsize(inode, 0);
- inode->i_blocks = 0;
}
}
if (cfile)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 143fa2e665ed..888f9e35f14b 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -219,13 +219,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
*/
if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
i_size_write(inode, fattr->cf_eof);
-
- /*
- * i_blocks is not related to (i_size / i_blksize),
- * but instead 512 byte (2**9) size is required for
- * calculating num blocks.
- */
- inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
+ inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes);
}
if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
@@ -3015,6 +3009,11 @@ void cifs_setsize(struct inode *inode, loff_t offset)
{
spin_lock(&inode->i_lock);
i_size_write(inode, offset);
+ /*
+ * Until we can query the server for actual allocation size,
+ * this is best estimate we have for blocks allocated for a file.
+ */
+ inode->i_blocks = CIFS_INO_BLOCKS(offset);
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
truncate_pagecache(inode, offset);
@@ -3087,14 +3086,6 @@ set_size_out:
if (rc == 0) {
netfs_resize_file(&cifsInode->netfs, size, true);
cifs_setsize(inode, size);
- /*
- * i_blocks is not related to (i_size / i_blksize), but instead
- * 512 byte (2**9) size is required for calculating num blocks.
- * Until we can query the server for actual allocation size,
- * this is best estimate we have for blocks allocated for a file
- * Number of blocks must be rounded up so size 1 is not 0 blocks
- */
- inode->i_blocks = (512 - 1 + size) >> 9;
}
return rc;
diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c
index 38d6d5538b96..53abb29fe71b 100644
--- a/fs/smb/client/smb1transport.c
+++ b/fs/smb/client/smb1transport.c
@@ -460,7 +460,7 @@ check_smb_hdr(struct smb_hdr *smb)
return 0;
/*
- * Windows NT server returns error resposne (e.g. STATUS_DELETE_PENDING
+ * Windows NT server returns error response (e.g. STATUS_DELETE_PENDING
* or STATUS_OBJECT_NAME_NOT_FOUND or ERRDOS/ERRbadfile or any other)
* for some TRANS2 requests without the RESPONSE flag set in header.
*/
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 98ac4e86bf99..509fcea28a42 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1497,6 +1497,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb2_file_network_open_info file_inf;
struct inode *inode;
+ u64 asize;
int rc;
rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
@@ -1520,14 +1521,9 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
inode_set_atime_to_ts(inode,
cifs_NTtimeToUnix(file_inf.LastAccessTime));
- /*
- * i_blocks is not related to (i_size / i_blksize),
- * but instead 512 byte (2**9) size is required for
- * calculating num blocks.
- */
- if (le64_to_cpu(file_inf.AllocationSize) > 4096)
- inode->i_blocks =
- (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9;
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ if (asize > 4096)
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
/* End of file and Attributes should not have to be updated on close */
spin_unlock(&inode->i_lock);
@@ -2204,14 +2200,6 @@ smb2_duplicate_extents(const unsigned int xid,
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
-
- /*
- * Although also could set plausible allocation size (i_blocks)
- * here in addition to setting the file size, in reflink
- * it is likely that the target file is sparse. Its allocation
- * size will be queried on next revalidate, but it is important
- * to make sure that file's cached size is updated immediately
- */
netfs_resize_file(netfs_inode(inode), dest_off + len, true);
cifs_setsize(inode, dest_off + len);
}
diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
index a72d7e42a6c2..58e5b8592da4 100644
--- a/fs/smb/server/mgmt/tree_connect.c
+++ b/fs/smb/server/mgmt/tree_connect.c
@@ -102,8 +102,10 @@ out_error:
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
{
- if (atomic_dec_and_test(&tcon->refcount))
+ if (atomic_dec_and_test(&tcon->refcount)) {
+ ksmbd_share_config_put(tcon->share_conf);
kfree(tcon);
+ }
}
static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
@@ -113,10 +115,11 @@ static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
- ksmbd_share_config_put(tree_conn->share_conf);
ksmbd_counter_dec(KSMBD_COUNTER_TREE_CONNS);
- if (atomic_dec_and_test(&tree_conn->refcount))
+ if (atomic_dec_and_test(&tree_conn->refcount)) {
+ ksmbd_share_config_put(tree_conn->share_conf);
kfree(tree_conn);
+ }
return ret;
}
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 9f7ff7491e9a..9c44e71e3c3b 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -126,6 +126,8 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
pr_err("The first operation in the compound does not have tcon\n");
return -EINVAL;
}
+ if (work->tcon->t_state != TREE_CONNECTED)
+ return -ENOENT;
if (tree_id != UINT_MAX && work->tcon->id != tree_id) {
pr_err("tree id(%u) is different with id(%u) in first operation\n",
tree_id, work->tcon->id);
@@ -1948,6 +1950,7 @@ out_err:
}
}
smb2_set_err_rsp(work);
+ conn->binding = false;
} else {
unsigned int iov_len;
@@ -2828,7 +2831,11 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}
- dh_info->fp->conn = conn;
+ if (dh_info->fp->conn) {
+ ksmbd_put_durable_fd(dh_info->fp);
+ err = -EBADF;
+ goto out;
+ }
dh_info->reconnected = true;
goto out;
}
@@ -5452,7 +5459,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
struct smb2_query_info_req *req,
struct smb2_query_info_rsp *rsp)
{
- struct ksmbd_session *sess = work->sess;
struct ksmbd_conn *conn = work->conn;
struct ksmbd_share_config *share = work->tcon->share_conf;
int fsinfoclass = 0;
@@ -5589,10 +5595,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
info = (struct object_id_info *)(rsp->Buffer);
- if (!user_guest(sess->user))
- memcpy(info->objid, user_passkey(sess->user), 16);
+ if (path.mnt->mnt_sb->s_uuid_len == 16)
+ memcpy(info->objid, path.mnt->mnt_sb->s_uuid.b,
+ path.mnt->mnt_sb->s_uuid_len);
else
- memset(info->objid, 0, 16);
+ memcpy(info->objid, &stfs.f_fsid, sizeof(stfs.f_fsid));
info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC);
info->extended_info.version = cpu_to_le32(1);
diff --git a/fs/tests/exec_kunit.c b/fs/tests/exec_kunit.c
index f412d1a0f6bb..1c32cac098cf 100644
--- a/fs/tests/exec_kunit.c
+++ b/fs/tests/exec_kunit.c
@@ -94,9 +94,6 @@ static const struct bprm_stack_limits_result bprm_stack_limits_results[] = {
{ { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3 + sizeof(void *)),
.argc = 0, .envc = 0 },
.expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) },
- { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * + sizeof(void *)),
- .argc = 0, .envc = 0 },
- .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) },
{ { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * _STK_LIM,
.argc = 0, .envc = 0 },
.expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) },